/* async_file_io
Provides a threadpool and asynchronous file i/o infrastructure based on Boost.ASIO, Boost.Iostreams and filesystem
(C) 2013-2015 Niall Douglas http://www.nedprod.com/
File Created: Mar 2013


Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/

#ifndef BOOST_AFIO_HEADER_INCLUDED

#ifdef DOXYGEN_SHOULD_SKIP_THIS
#define BOOST_AFIO_HEADERS_ONLY 0
#define BOOST_AFIO_USE_BOOST_THREAD 0
#define BOOST_AFIO_USE_BOOST_FILESYSTEM 1
#define ASIO_STANDALONE 0
#endif

#include "config.hpp"

// clang-format off
#ifdef DOXYGEN_SHOULD_SKIP_THIS
#undef BOOST_AFIO_V2_NAMESPACE
#undef BOOST_AFIO_V2_NAMESPACE_BEGIN
#undef BOOST_AFIO_V2_NAMESPACE_END
#undef BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC
#undef BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC

#define BOOST_AFIO_V2_NAMESPACE boost::afio
#define BOOST_AFIO_V2_NAMESPACE_BEGIN namespace boost { namespace afio {
#define BOOST_AFIO_V2_NAMESPACE_END } }
#define BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC
#define BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC virtual
#endif
// clang-format on

#ifndef BOOST_AFIO_AFIO_H
#define BOOST_AFIO_AFIO_H

#include "detail/Undoer.hpp"
#include "detail/ErrorHandling.hpp"
#include "detail/Utility.hpp"
#include <algorithm> // Boost.ASIO needs std::min and std::max
#include <exception>
#include <iostream>
#include <type_traits>

/*! \brief Validate inputs at the point of instantiation.

Turns on the checking of inputs for validity and throwing of exception conditions
at the point of instantiation rather than waiting until those inputs are sent
for dispatch. This, being very useful for debugging, defaults to 1 except when
`NDEBUG` is defined i.e. final release builds.
\ingroup macros
*/
#ifndef BOOST_AFIO_VALIDATE_INPUTS
#ifndef NDEBUG
#define BOOST_AFIO_VALIDATE_INPUTS 1
#else
#define BOOST_AFIO_VALIDATE_INPUTS 0
#endif
#endif

#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable: 4251) // type needs to have dll-interface to be used by clients of class
#endif

/*! \file afio.hpp
\brief Provides a batch asynchronous file i/o implementation based on Boost.ASIO
*/
/*! \def BOOST_AFIO_HEADERS_ONLY
\brief Determines if AFIO is compiled as headers only. Defaults to 1.
*/
/*! \def BOOST_AFIO_USE_BOOST_THREAD
\brief Determines if AFIO is bound against Boost.Thread or the C++ 11 STL thread. Defaults to 0.
*/
/*! \def BOOST_AFIO_USE_BOOST_FILESYSTEM
\brief Determines if AFIO is bound against Boost.Filesystem or the C++ 1z Filesystem TS. Defaults to 1 unless on VS2015 which provides a full Filesystem TS implementation.
*/
/*! \def ASIO_STANDALONE
\brief Determines if AFIO is bound against standalone ASIO or Boost.ASIO. Defaults to undefined, and therefore Boost.ASIO.
*/

BOOST_AFIO_V2_NAMESPACE_BEGIN

// This isn't consistent on MSVC so hard code it
typedef unsigned long long off_t;

//! \brief The namespace containing Boost.ASIO internal details
namespace detail
{
    template<class R> class enqueued_task_impl
    {
    protected:
        struct Private
        {
            std::function<R()> task;
            promise<R> r;
            shared_future<R> f;
            bool autoset;
            atomic<int> done;
            Private(std::function<R()> _task) : task(std::move(_task)), f(r.get_future().share()), autoset(true), done(0) { }
        };
        std::shared_ptr<Private> p;
        void validate() const { assert(p); /*if(!p) abort();*/ }
    public:
        //! Default constructor
        enqueued_task_impl(std::function<R()> _task=std::function<R()>()) : p(std::make_shared<Private>(std::move(_task))) { }
        //! Returns true if valid
        bool valid() const noexcept{ return p.get()!=nullptr; }
        //! Swaps contents with another instance
        void swap(enqueued_task_impl &o) noexcept{ p.swap(o.p); }
        //! Resets the contents
        void reset() { p.reset(); }
        //! Sets the task
        void set_task(std::function<R()> _task) { p->task=std::move(_task); }
        //! Returns the shared stl_future corresponding to the stl_future return value of the task
        const shared_future<R> &get_future() const { validate(); return p->f; }
        //! Sets the shared stl_future corresponding to the stl_future return value of the task.
        template<class T> void set_future_value(T v)
        {
            int _=0;
            validate();
            if(!p->done.compare_exchange_strong(_, 1))
                return;
            p->r.set_value(std::move(v));
        }
        void set_future_value()
        {
            int _=0;
            validate();
            if(!p->done.compare_exchange_strong(_, 1))
                return;
            p->r.set_value();
        }
        //! Sets the shared stl_future corresponding to the stl_future return value of the task.
        void set_future_exception(exception_ptr e)
        {
            int _=0;
            validate();
            if(!p->done.compare_exchange_strong(_, 1))
                return;
            p->r.set_exception(e);
        }
        //! Disables the task setting the shared stl_future return value.
        void disable_auto_set_future(bool v=true) { validate(); p->autoset=!v; }
    };
}

template<class R> class enqueued_task;
/*! \class enqueued_task<R()>
\tparam "class R" The return type of the callable which must be without parameters.
\brief Effectively our own custom std::packaged_task<>, with copy semantics and letting us early set value to significantly improve performance

Unlike `std::packaged_task<>`, this custom variant is copyable though each copy always refers to the same
internal state. Early stl_future value setting is possible, with any subsequent value setting including that
by the function being executed being ignored. Note that this behaviour opens the potential to lose exception
state - if you set the stl_future value early and then an exception is later thrown, the exception is swallowed.

*/
// Can't have args in callable type as that segfaults VS2010
template<class R> class enqueued_task<R()> : public detail::enqueued_task_impl<R>
{
    typedef detail::enqueued_task_impl<R> Base;
public:
    //! Default constructor
    enqueued_task(std::function<R()> _task=std::function<R()>()) : Base(std::move(_task)) { }
    //! Invokes the callable, setting the shared stl_future to the value it returns
    void operator()()
    {
        auto _p(Base::p);
        Base::validate();
        if(!_p->task) abort();
        try
        {
            auto v(_p->task());
            if(_p->autoset && !_p->done) Base::set_future_value(v);
        }
        catch(...)
        {
            if(_p->done)
            {
              BOOST_AFIO_LOG_FATAL_EXIT(detail::output_exception_info << " thrown up to enqueued_task<> after stl_future set." << std::endl);
              BOOST_AFIO_THROW_FATAL(std::runtime_error("Exception thrown up to enqueued_task<> after stl_future set."));
            }
            if(_p->autoset && !_p->done) 
            {
                auto e(current_exception());
                Base::set_future_exception(e);
            }
        }
        // Free any bound parameters in task to save memory
        _p->task=std::function<R()>();
    }
};
template<> class enqueued_task<void()> : public detail::enqueued_task_impl<void>
{
    typedef detail::enqueued_task_impl<void> Base;
public:
    //! Default constructor
    enqueued_task(std::function<void()> _task=std::function<void()>()) : Base(std::move(_task)) { }
    //! Invokes the callable, setting the stl_future to the value it returns
    void operator()()
    {
        auto _p(Base::p);
        Base::validate();
        if(!_p->task) abort();
        try
        {
            _p->task();
            if(_p->autoset && !_p->done) Base::set_future_value();
        }
        catch(...)
        {
            if(_p->done)
            {
              BOOST_AFIO_LOG_FATAL_EXIT(detail::output_exception_info << " thrown up to enqueued_task<> after stl_future set." << std::endl);
              BOOST_AFIO_THROW_FATAL(std::runtime_error("Exception thrown up to enqueued_task<> after stl_future set."));
            }
            if(_p->autoset && !_p->done)
            {
                auto e(current_exception());
                Base::set_future_exception(e);
            }
        }
        // Free any bound parameters in task to save memory
        _p->task=std::function<void()>();
    }
};
/*! \class thread_source
\brief Abstract base class for a source of thread workers

Note that in Boost 1.54, and possibly later versions, `asio::io_service` on Windows appears to dislike being
destructed during static data deinit, hence why this inherits from `std::enable_shared_from_this<>` in order that it
may be reference count deleted before static data deinit occurs.
*/
class thread_source : public std::enable_shared_from_this<thread_source>
{
protected:
    asio::io_service &service;
    thread_source(asio::io_service &_service) : service(_service) { }
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~thread_source() { }
    thread_source &operator=(const thread_source &) = delete;
public:
    //! Returns the underlying io_service
    asio::io_service &io_service() { return service; }
    //! Sends a task to the thread pool for execution \tparam "class R" The return type of the enqueued task
    template<class R> void enqueue(enqueued_task<R> task)
    {
        service.post(task);
    }
    //! Sends some callable entity to the thread pool for execution \return An enqueued task for the enqueued callable \tparam "class F" Any callable type with signature R(void) \param f Any instance of a callable type
    template<class F> shared_future<typename std::result_of<F()>::type> enqueue(F f)
    {
        typedef typename std::result_of<F()>::type R;
        enqueued_task<R()> out(std::move(f));
        auto ret(out.get_future());
        service.post(out);
        return ret;
    }
};

/*! \class std_thread_pool
\brief A very simple thread pool based on std::thread or boost::thread

This instantiates a `asio::io_service` and a latchable `asio::io_service::work` to keep any threads working until the instance is destructed.
*/
class std_thread_pool : public thread_source {
    class worker
    {
        std_thread_pool *pool;
    public:
        explicit worker(std_thread_pool *p) : pool(p) { }
        void operator()()
        {
            detail::set_threadname("boost::afio::std_thread_pool worker");
            try
            {
                pool->service.run();
            }
            catch(...)
            {
              BOOST_AFIO_LOG_FATAL_EXIT("WARNING: ASIO exits via " << detail::output_exception_info << " which shouldn't happen." << std::endl);
            }
        }
    };
    friend class worker;

    asio::io_service service;
    std::unique_ptr<asio::io_service::work> working;
    std::vector< std::unique_ptr<thread> > workers;
public:
    /*! \brief Constructs a thread pool of \em no workers
    \param no The number of worker threads to create
    */
    explicit std_thread_pool(size_t no) : thread_source(service), working(detail::make_unique<asio::io_service::work>(service))
    {
        add_workers(no);
    }
    //! Adds more workers to the thread pool \param no The number of worker threads to add
    void add_workers(size_t no)
    {
        workers.reserve(workers.size()+no);
        for(size_t n=0; n<no; n++)
            workers.push_back(detail::make_unique<thread>(worker(this)));
    }
    //! Destroys the thread pool, waiting for worker threads to exit beforehand.
    void destroy()
    {
        if(!service.stopped())
        {
            // Tell the threads there is no more work to do
            working.reset();
            for(auto &i: workers) { i->join(); }
            workers.clear();
            // For some reason ASIO occasionally thinks there is still more work to do
            if(!service.stopped())
                service.run();
            service.stop();
            service.reset();
        }
    }
    ~std_thread_pool() final
    {
        destroy();
    }
};
/*! \brief Returns the process threadpool

On first use, this instantiates a default std_thread_pool running `BOOST_AFIO_MAX_NON_ASYNC_QUEUE_DEPTH` threads which will remain until its shared count reaches zero.
\ingroup process_threadpool
*/
BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC std::shared_ptr<std_thread_pool> process_threadpool();


class dispatcher;
using dispatcher_ptr = std::shared_ptr<dispatcher>;
template<class T=void> class future;
struct path_req;
template<class T> struct io_req;
struct enumerate_req;
struct lock_req;
namespace detail {
    struct async_io_handle_posix;
    struct async_io_handle_windows;
    struct dispatcher_p;
    class async_file_io_dispatcher_compat;
    class async_file_io_dispatcher_windows;
    class async_file_io_dispatcher_linux;
    class async_file_io_dispatcher_qnx;
    struct immediate_async_ops;
    template<bool for_writing> class io_req_impl;
}

//! \brief The types of path normalisation available
enum class path_normalise
{
  dos,         //!< Return the shortest normalised path possible (usually a drive letter prefix). This is a traditional DOS style path.
  guid_volume, //!< Return the volume as a GUID. This eliminates problems with drive letters vanishing or being ambiguous. Anything accepting a Win32 path can accept one of these.
  guid_all     //!< Return the whole path as a GUID. This eliminates problems with long paths or if the file is renamed. Note this may cause the creation of a GUID for the file. Anything accepting a Win32 path can accept one of these.
};

/*! \class path
\brief An AFIO filesystem path, a thin wrapper of filesystem::path used to mark when a
filesystem path has been prepared for AFIO usage. Note that on Windows this exclusively
refers to a case sensitive NT kernel path, not a Win32 path (Win32 paths are converted in the constructor).

\qbk{
[include generated/struct_path_1_1make_absolute.qbk]
[include generated/group_normalise_path.qbk]
[include generated/struct_path_hash.qbk]
}
*/
class path : protected filesystem::path
{
  void int_regularise()
  {
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 6326) // comparison of constants
#endif
    if(preferred_separator!='/')
      make_preferred();
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#ifdef WIN32
    // Need to strip off any win32 prefixing, and instead prefix any drive letters
    bool isExtendedPath=false, isDevicePath=false;
    if(native().size()>=4)
    {
#ifndef NDEBUG
      if(native()[0]=='\\' && native()[1]=='?' && native()[2]=='?' && native()[3]=='\\')
      {
        assert(!(native()[0]=='\\' && native()[1]=='?' && native()[2]=='?' && native()[3]=='\\'));
      }
#endif
      isExtendedPath=(native()[0]=='\\' && native()[1]=='\\' && native()[2]=='?' && native()[3]=='\\');
      isDevicePath=(native()[0]=='\\' && native()[1]=='\\' && native()[2]=='.' && native()[3]=='\\');
    }
    bool hasDriveLetter=(isalpha(native()[((int) isExtendedPath+(int) isDevicePath)*4+0]) && native()[((int) isExtendedPath+(int) isDevicePath)*4+1]==':');
    if(hasDriveLetter && (isExtendedPath || isDevicePath))
    {
      filesystem::path::string_type &me=const_cast<filesystem::path::string_type &>(native());
      me[1]=me[2]='?';
    }
    else if(hasDriveLetter)
    {
      filesystem::path::string_type &me=const_cast<filesystem::path::string_type &>(native());
      me=L"\\??\\"+me;
    }
    else if(isExtendedPath || isDevicePath)
    {
      filesystem::path::string_type &me=const_cast<filesystem::path::string_type &>(native());
      me=me.substr(isDevicePath ? 3 : 4);
    }
#endif
  }
  friend struct detail::async_io_handle_windows;
  struct direct { };
  path(filesystem::path &&p, direct) : filesystem::path(std::move(p)) { }
public:
  typedef filesystem::path::value_type value_type;
  typedef filesystem::path::string_type string_type;
  using filesystem::path::preferred_separator;
    //! Makes a path absolute
  struct make_absolute;

  //! \constr
  path() {}
  //! \cconstr
  path(const path &p) : filesystem::path(p) { }
  //! Converts a filesystem::path to AFIO format
  path(const filesystem::path &p) : filesystem::path(p) { int_regularise(); }
  //! Converts a filesystem::path to AFIO format
  path(const char *p) : filesystem::path(p) { int_regularise(); }
#ifdef WIN32
  //! Converts a filesystem::path to AFIO format
  path(const wchar_t *p) : filesystem::path(p) { int_regularise(); }
  //! Converts a filesystem::path to AFIO format
  path(const std::string &p) : filesystem::path(p) { int_regularise(); }
#endif
  //! Converts a filesystem::path to AFIO format
  path(const string_type &p) : filesystem::path(p) { int_regularise(); }
  //! \mconstr
  path(path &&p) noexcept : filesystem::path(std::move(p)) { }
  //! Converts a filesystem::path to AFIO format
  path(filesystem::path &&p) : filesystem::path(std::move(p)) { int_regularise(); }
#ifdef WIN32
  //! Converts a filesystem::path to AFIO format
  path(std::string &&p) : filesystem::path(std::move(p)) { int_regularise(); }
#endif
  //! Converts a filesystem::path to AFIO format
  path(string_type &&p) : filesystem::path(std::move(p)) { int_regularise(); }
  //! Converts source to AFIO path format
  //template<class Source> path(const Source &source) : filesystem::path(source) { int_regularise(); }
  //! Converts source to AFIO path format
  template <class InputIterator> path(InputIterator begin, InputIterator end) : filesystem::path(begin, end) { int_regularise(); }
  //! \cassign
  path& operator=(const path& p) { filesystem::path::operator=(filesystem::path(p)); return *this; }
  //! \massign
  path& operator=(path&& p) noexcept { filesystem::path::operator=(static_cast<filesystem::path &&>(p)); return *this; }
  //! Converts source to AFIO path format
  //template <class Source> path& operator=(Source const& source) { filesystem::path::operator=(source); int_regularise(); return *this; }

  template <class Source>
    path& assign(Source const& source) { filesystem::path::assign(source); return *this; }
  template <class InputIterator>
    path& assign(InputIterator begin, InputIterator end) { filesystem::path::assign(begin, end); return *this; }
  path& operator/=(const path& p) { filesystem::path::operator/=(filesystem::path(p)); return *this; }
  template <class Source>
    path& operator/=(Source const& source) { filesystem::path::operator/=(source); return *this; }
  template <class Source>
    path& append(Source const& source) { filesystem::path::append(source); return *this; }
  template <class InputIterator>
    path& append(InputIterator begin, InputIterator end) { filesystem::path::append(begin, end); return *this; }

  path& operator+=(const path& x) { filesystem::path::operator+=(filesystem::path(x)); return *this; }
  path& operator+=(const string_type& x) { filesystem::path::operator+=(x); return *this; }
  path& operator+=(const value_type* x) { filesystem::path::operator+=(x); return *this; }
  path& operator+=(value_type x) { filesystem::path::operator+=(x); return *this; }
  template <class Source>
    path& operator+=(Source const& x) { filesystem::path::operator+=(x); return *this; }
  template <class Source>
    path& concat(Source const& x) { filesystem::path::concat(x); return *this; }
  template <class InputIterator>
    path& concat(InputIterator begin, InputIterator end) { filesystem::path::concat(begin, end); return *this; }
  
  using filesystem::path::clear;
  path& make_preferred() { filesystem::path::make_preferred(); return *this; }
  path& remove_filename() { filesystem::path::remove_filename(); return *this; }
  path& replace_extension(const path& new_extension = path()) { filesystem::path::replace_extension(filesystem::path(new_extension)); return *this; }
  using filesystem::path::swap;

  using filesystem::path::native;
  using filesystem::path::c_str;
  using filesystem::path::string;
  using filesystem::path::wstring;
  using filesystem::path::generic_string;
  using filesystem::path::compare;

  path  root_name() const { return path(filesystem::path::root_name(), direct()); }
  path  root_directory() const { return path(filesystem::path::root_directory(), direct()); }
  path  root_path() const { return path(filesystem::path::root_path(), direct()); }
  path  relative_path() const { return path(filesystem::path::relative_path(), direct()); }
  path  parent_path() const { return path(filesystem::path::parent_path(), direct()); }
#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
  path  filename() const { return path(filesystem::path::leaf(), direct()); }
#else
  path  filename() const { return path(filesystem::path::filename(), direct()); }
#endif
  path  stem() const { return path(filesystem::path::stem(), direct()); }
  path  extension() const { return path(filesystem::path::extension(), direct()); }

  using filesystem::path::empty;
  using filesystem::path::has_root_name;
  using filesystem::path::has_root_directory;
  using filesystem::path::has_root_path;
  using filesystem::path::has_relative_path;
  using filesystem::path::has_parent_path;
  using filesystem::path::has_filename;
  using filesystem::path::has_stem;
  using filesystem::path::has_extension;
  using filesystem::path::is_absolute;
  using filesystem::path::is_relative;

  // TODO FIXME: Need our own iterator here
  typedef filesystem::path::iterator iterator;
  typedef filesystem::path::const_iterator const_iterator;

  iterator begin() const { return filesystem::path::begin(); }
  iterator end() const { return filesystem::path::end(); }
  
  /*! \brief Return a normalised filesystem::path from an AFIO path.

  On POSIX this passes through its input unchanged.

  On Windows AFIO exclusively uses NT kernel paths which are not necessarily trivially convertible
  to Win32 paths. As an example, the Win32 path `C:\Foo` might be `\??\C:\Foo` or even
  `\Device\HarddiskVolume1\Foo`. This function will convert any NT kernel path into
  something which can be fed to normal Win32 APIs quickly, though note that the
  output path will be rejected by most other APIs as invalid. If you need a Win32
  path which is completely valid, use normalise_path().
  */
  filesystem::path filesystem_path() const
  {
#ifdef WIN32
    bool isSymlinkedDosPath=(native()[0]=='\\' && native()[1]=='?' && native()[2]=='?' && native()[3]=='\\');
    if(isSymlinkedDosPath)
    {
      filesystem::path::string_type p(native());
      p[1]='\\';
      return p;
    }
    else
      return filesystem::path(L"\\\\.")/filesystem::path(*this);
#else
    return *this;
#endif
  }
  friend inline bool operator<(const path& lhs, const path& rhs);
  friend inline bool operator<=(const path& lhs, const path& rhs);
  friend inline bool operator>(const path& lhs, const path& rhs);
  friend inline bool operator>=(const path& lhs, const path& rhs);
  friend inline bool operator==(const path& lhs, const path& rhs);
  friend inline bool operator!=(const path& lhs, const path& rhs);
  friend inline path operator/(const path& lhs, const path& rhs);
  friend inline std::ostream &operator<<(std::ostream &s, const path &p);
  friend struct path_hash;
#ifdef WIN32
#ifdef _MSC_VER
  friend BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC filesystem::path normalise_path(path p, path_normalise type);
#else
  friend filesystem::path normalise_path(path p, path_normalise type);
#endif
#else
  friend inline filesystem::path normalise_path(path p, path_normalise type);
#endif
};
inline bool operator<(const path& lhs, const path& rhs) { return filesystem::path(lhs)<filesystem::path(rhs); }
inline bool operator<=(const path& lhs, const path& rhs) { return filesystem::path(lhs)<=filesystem::path(rhs); }
inline bool operator>(const path& lhs, const path& rhs) { return filesystem::path(lhs)>filesystem::path(rhs); }
inline bool operator>=(const path& lhs, const path& rhs) { return filesystem::path(lhs)>=filesystem::path(rhs); }
inline bool operator==(const path& lhs, const path& rhs) { return filesystem::path(lhs)==filesystem::path(rhs); }
inline bool operator!=(const path& lhs, const path& rhs) { return filesystem::path(lhs)!=filesystem::path(rhs); }
inline path operator/(const path& lhs, const path& rhs) { return path(filesystem::path(lhs)/filesystem::path(rhs), path::direct()); }
inline std::ostream &operator<<(std::ostream &s, const path &p) { return s << filesystem::path(p); }
//! Makes a path absolute according to the current working directory
struct path::make_absolute : public path
{
  make_absolute(const path &p) : path(p)
  {
    if(native()[0]!=preferred_separator)
      *this=filesystem::absolute(std::move(*this));
  }
  make_absolute(path &&p) : path(std::move(p))
  {
    if(native()[0]!=preferred_separator)
      *this=filesystem::absolute(std::move(*this));
  }
  template<class T, typename=typename std::enable_if<std::is_constructible<filesystem::path, T>::value>::type> make_absolute(T &&p) : path(filesystem::absolute(std::forward<T>(p))) { }
};
/*! \brief A hasher for path
*/
struct path_hash
{
  std::hash<path::string_type> hasher;
public:
    size_t operator()(const path &p) const
    {
      return hasher(p.native());
    }
};

/*! \brief Return a normalised filesystem::path from an AFIO path.

On POSIX this passes through its input unchanged.

On Windows AFIO exclusively uses NT kernel paths which are not necessarily trivially convertible
to Win32 paths. As an example, the Win32 path `C:\\Foo` might be `\\??\\C:\\Foo` or even
`\\Device\\HarddiskVolume1\\Foo`. This function will convert any NT kernel path into
something which can be fed to normal Win32 APIs - a drive letter if available, else a GUID volume
path, and with an extended path prefix if the path is sufficiently long. It also scans the path
for characters illegal under Win32 or paths which begin with a space or end with a period, and
will extended path prefix such paths as well.

\ingroup normalise_path
\param p Path to be normalised
\param type A path_normalise enum
*/
#ifdef WIN32
BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC filesystem::path normalise_path(path p, path_normalise type=path_normalise::dos);
#else
inline filesystem::path normalise_path(path p, path_normalise type=path_normalise::dos) { return p; }
#endif


#define BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(type) \
inline constexpr type operator&(type a, type b) \
{ \
    return static_cast<type>(static_cast<size_t>(a) & static_cast<size_t>(b)); \
} \
inline constexpr type operator|(type a, type b) \
{ \
    return static_cast<type>(static_cast<size_t>(a) | static_cast<size_t>(b)); \
} \
inline constexpr type operator~(type a) \
{ \
    return static_cast<type>(~static_cast<size_t>(a)); \
} \
inline constexpr bool operator!(type a) \
{ \
    return 0==static_cast<size_t>(a); \
}



/*! \enum file_flags
\brief Bitwise file and directory open flags
\ingroup file_flags
*/
enum class file_flags : size_t
{
    none=0,             //!< No flags set
    read=1,             //!< Read access
    write=2,            //!< Write access
    read_write=3,       //!< Read and write access
    append=4,           //!< Append only
    truncate=8,         //!< Truncate existing file to zero
    create=16,          //!< Open and create if doesn't exist. Always creates sparse files if possible.
    create_only_if_not_exist=32, //!< Create and open only if doesn't exist
    create_compressed=64, //!< Create a compressed file, needs to be combined with one of the other create flags. Only succeeds if supported by the underlying filing system.

    will_be_sequentially_accessed=128, //!< Will be \em exclusively either read or written sequentially. If you're exclusively writing sequentially, \em strongly consider turning on `os_direct` too.
    will_be_randomly_accessed=256, //!< Will be randomly accessed, so don't bother with read-ahead. If you're using this, \em strongly consider turning on `os_direct` too.
    no_sparse=512,      //!< Don't create sparse files. May be ignored by some filing systems (e.g. ext4).

    hold_parent_open=(1<<10),        //!< Hold a file handle open to the containing directory of each open file for fast directory enumeration and fast relative path ops.
    unique_directory_handle=(1<<11), //!< Return a unique directory handle rather than a shared directory handle
    no_race_protection=(1<<12),      //!< Skip taking steps to avoid destruction of data due to filing system races. Most of the performance benefit of enabling this goes away if you enable HoldParentOpen instead, so be especially careful when considering turning this on.
    temporary_file=(1<<13),          //!< On some systems causes dirty cache data to not be written to physical storage until file close. Useful for temporary files and lock files, especially on Windows when combined with `delete_on_close` as this avoids an fsync of the containing directory on file close.
    delete_on_close=(1<<14),         //!< Only when combined with `create_only_if_not_exist`, deletes the file on close. This is especially useful on Windows with temporary and lock files where normally closing a file is an implicit fsync of its containing directory. Note on POSIX this unlinks the file on first close by AFIO, whereas on Windows the operating system unlinks the file on last close including sudden application exit. Note also that AFIO permits you to delete files which are currently open on Windows and the file entry disappears immediately just as on POSIX.

    os_direct=(1<<16),      //!< Bypass the OS file buffers (only really useful for writing large files, or a lot of random reads and writes. Note you must 4Kb align everything if this is on). Be VERY careful mixing this with memory mapped files.
    os_lockable=(1<<17),    // Deliberately undocumented

    always_sync=(1<<24),    //!< Ask the OS to not complete until the data is on the physical storage. Some filing systems do much better with this than `sync_on_close`.
    sync_on_close=(1<<25),  //!< Automatically initiate an asynchronous flush just before file close, and fuse both operations so both must complete for close to complete.

    int_hold_parent_open_nested=(1<<27), //!< Internal use only. Don't use.
    int_file_share_delete=(1<<28), //!< Internal use only. Don't use.
    int_opening_link=(1<<29), //!< Internal use only. Don't use.
    int_opening_dir=(1<<30) //!< Internal use only. Don't use.
};
BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(file_flags)

/*! \enum async_op_flags
\brief Bitwise async_op_flags flags
\ingroup async_op_flags
*/
enum class async_op_flags : size_t
{
    none=0,                 //!< No flags set
    immediate=1             //!< Call chained completion immediately instead of scheduling for later. Make SURE your completion can not block!
};
BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(async_op_flags)

namespace detail {
    /*! \enum OpType
    \brief The type of operation
    */
    enum class OpType
    {
        Unknown,
        UserCompletion,
        dir,
        rmdir,
        file,
        rmfile,
        symlink,
        rmsymlink,
        sync,
        close,
        read,
        write,
        truncate,
        barrier,
        enumerate,
        adopt,
        zero,
        extents,
        statfs,
        lock,

        Last
    };
    static const char *optypes[]={
        "unknown",
        "UserCompletion",
        "dir",
        "rmdir",
        "file",
        "rmfile",
        "symlink",
        "rmsymlink",
        "sync",
        "close",
        "read",
        "write",
        "truncate",
        "barrier",
        "enumerate",
        "adopt",
        "zero",
        "extents",
        "statfs",
        "lock"
    };
    static_assert(static_cast<size_t>(OpType::Last)==sizeof(optypes)/sizeof(*optypes), "You forgot to fix up the strings matching OpType");

    enum class unit_testing_flags : size_t
    {
        none=0,                  //!< No flags set
        no_symbol_lookup=(1<<0)  //!< Don't bother looking up symbols in stack backtracing as it's horribly slow on POSIX especially
    };
    BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(unit_testing_flags)
}

class handle;
//! A type alias to a shared pointer to handle
using handle_ptr = std::shared_ptr<handle>;

/*! \enum metadata_flags
\brief Bitflags for availability of metadata from `struct stat_t`
\ingroup metadata_flags

See __afio_stat_t__ for explanation of meaning.
*/
enum class metadata_flags : size_t
{
    None=0,
    dev=1<<0,
    ino=1<<1,
    type=1<<2,
    perms=1<<3,
    nlink=1<<4,
    uid=1<<5,
    gid=1<<6,
    rdev=1<<7,
    atim=1<<8,
    mtim=1<<9,
    ctim=1<<10,
    size=1<<11,
    allocated=1<<12,
    blocks=1<<13,
    blksize=1<<14,
    flags=1<<15,
    gen=1<<16,
    birthtim=1<<17,
    sparse=1<<24,
    compressed=1<<25,
    reparse_point=1<<26,
    All=(size_t)-1       //!< Return the maximum possible metadata.
};
BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(metadata_flags)
/*! \struct stat_t
\brief Metadata about a directory entry

This structure looks somewhat like a `struct stat`, and indeed it was derived from BSD's `struct stat`.
However there are a number of changes to better interoperate with modern practice, specifically:

- inode value containers are forced to 64 bits.
- Timestamps use C++11's `std::chrono::system_clock::time_point` or Boost equivalent. The resolution
of these may or may not equal what a `struct timespec` can do depending on your STL.
- The type of a file, which is available on Windows and on POSIX without needing an additional
syscall, is provided by `st_type` which is one of the values from `filesystem::file_type`.
- As type is now separate from permissions, there is no longer a `st_mode`, instead being a
`st_perms` which is solely the permissions bits. If you want to test permission bits in `st_perms`
but don't want to include platform specific headers, note that `filesystem::perms` contains
definitions of the POSIX permissions flags.
- The st_sparse and st_compressed flags indicate if your file is sparse and/or compressed, or if
the directory will compress newly created files by default. Note that on POSIX, a file is sparse
if and only if st_allocated < st_size which can include compressed files if that filing system is mounted
with compression enabled (e.g. ZFS with ZLE compression which elides runs of zeros).
- The st_reparse_point is a Windows only flag and is never set on POSIX, even on a NTFS volume.
*/
struct stat_t
{
#ifndef WIN32
    uint64_t        st_dev;                       /*!< inode of device containing file (POSIX only) */
#endif
    uint64_t        st_ino;                       /*!< inode of file                   (Windows, POSIX) */
    filesystem::file_type st_type;                /*!< type of file                    (Windows, POSIX) */
#ifndef WIN32
#ifndef DOXYGEN_SHOULD_SKIP_THIS
    uint16_t        st_perms;
#else
    filesystem::perms st_perms;                   /*!< uint16_t bitfield perms of file (POSIX only) */
#endif
#endif
    int16_t         st_nlink;                     /*!< number of hard links            (Windows, POSIX) */
#ifndef WIN32
    int16_t         st_uid;                       /*!< user ID of the file             (POSIX only) */
    int16_t         st_gid;                       /*!< group ID of the file            (POSIX only) */
    dev_t           st_rdev;                      /*!< id of file if special           (POSIX only) */
#endif
    chrono::system_clock::time_point st_atim;     /*!< time of last access             (Windows, POSIX) */
    chrono::system_clock::time_point st_mtim;     /*!< time of last data modification  (Windows, POSIX) */
    chrono::system_clock::time_point st_ctim;     /*!< time of last status change      (Windows, POSIX) */
    off_t           st_size;                      /*!< file size, in bytes             (Windows, POSIX) */
    off_t           st_allocated;                 /*!< bytes allocated for file        (Windows, POSIX) */
    off_t           st_blocks;                    /*!< number of blocks allocated      (Windows, POSIX) */
    uint16_t        st_blksize;                   /*!< block size used by this device  (Windows, POSIX) */
    uint32_t        st_flags;                     /*!< user defined flags for file     (FreeBSD, OS X, zero otherwise) */
    uint32_t        st_gen;                       /*!< file generation number          (FreeBSD, OS X, zero otherwise)*/
    chrono::system_clock::time_point st_birthtim; /*!< time of file creation           (Windows, FreeBSD, OS X, zero otherwise) */

    unsigned        st_sparse : 1;                /*!< if this file is sparse, or this directory capable of sparse files (Windows, POSIX) */
    unsigned        st_compressed : 1;            /*!< if this file is compressed, or this directory capable of compressed files (Windows) */
    unsigned        st_reparse_point : 1;         /*!< if this file or directory is a reparse point (Windows) */
    
    //! Constructs a UNINITIALIZED instance i.e. full of random garbage
    stat_t() { }
    //! Constructs a zeroed instance
    stat_t(std::nullptr_t) :
#ifndef WIN32
        st_dev(0),
#endif
        st_ino(0),
#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
        st_type(filesystem::file_type::type_unknown),
#else
        st_type(filesystem::file_type::unknown),
#endif
#ifndef WIN32
        st_perms(0),
#endif
        st_nlink(0),
#ifndef WIN32
        st_uid(0), st_gid(0), st_rdev(0),
#endif
        st_size(0), st_allocated(0), st_blocks(0), st_blksize(0), st_flags(0), st_gen(0), st_sparse(0), st_compressed(0), st_reparse_point(0) { }
};

/*! \enum fs_metadata_flags
\brief Bitflags for availability of metadata from `struct statfs_t`
\ingroup fs_metadata_flags
*/
enum class fs_metadata_flags : size_t
{
    None=0,
    flags=1<<1,
    bsize=1<<2,
    iosize=1<<3,
    blocks=1<<4,
    bfree=1<<5,
    bavail=1<<6,
    files=1<<7,
    ffree=1<<8,
    namemax=1<<9,
    owner=1<<10,
    fsid=1<<11,
    fstypename=1<<12,
    mntfromname=1<<13,
    mntonname=1<<14,
    All=(size_t)-1       //!< Return the maximum possible metadata.
};
BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(fs_metadata_flags)
/*! \struct statfs_t
\brief Metadata about a filing system. Unsupported entries are -1.

\qbk{
[include generated/struct_statfs_t_1_1f_flags_t.qbk]
}
*/
struct statfs_t
{
     struct f_flags_t
     {
        uint32_t rdonly : 1;          //!< Filing system is read only                                      (Windows, POSIX)
        uint32_t noexec : 1;          //!< Filing system cannot execute programs                           (POSIX)
        uint32_t nosuid : 1;          //!< Filing system cannot superuser                                  (POSIX)
        uint32_t acls : 1;            //!< Filing system provides ACLs                                     (Windows, POSIX)
        uint32_t xattr : 1;           //!< Filing system provides extended attributes                      (Windows, POSIX)
        uint32_t compression : 1;     //!< Filing system provides whole volume compression                 (Windows, POSIX)
        uint32_t extents : 1;         //!< Filing system provides extent based file storage (sparse files) (Windows, POSIX)
        uint32_t filecompression : 1; //!< Filing system provides per-file selectable compression          (Windows)
     } f_flags;                           /*!< copy of mount exported flags       (Windows, POSIX) */
     uint64_t f_bsize;                    /*!< fundamental filesystem block size  (Windows, POSIX) */
     uint64_t f_iosize;                   /*!< optimal transfer block size        (Windows, POSIX) */
     uint64_t f_blocks;                   /*!< total data blocks in filesystem    (Windows, POSIX) */
     uint64_t f_bfree;                    /*!< free blocks in filesystem          (Windows, POSIX) */
     uint64_t f_bavail;                   /*!< free blocks avail to non-superuser (Windows, POSIX) */
     uint64_t f_files;                    /*!< total file nodes in filesystem     (POSIX) */
     uint64_t f_ffree;                    /*!< free nodes avail to non-superuser  (POSIX) */
     uint32_t f_namemax;                  /*!< maximum filename length            (Windows, POSIX) */
#ifndef WIN32
     int16_t  f_owner;                    /*!< user that mounted the filesystem   (BSD, OS X) */
#endif
     uint64_t f_fsid[2];                  /*!< filesystem id                      (Windows, POSIX) */
     std::string f_fstypename;            /*!< filesystem type name               (Windows, POSIX) */
     std::string f_mntfromname;           /*!< mounted filesystem                 (Windows, POSIX) */
     path f_mntonname;        /*!< directory on which mounted         (Windows, POSIX) */
     statfs_t()
     {
       size_t frontbytes=((char *) &f_fstypename)-((char *) this);
       memset(this, 0xff, frontbytes);
       memset(this, 0, sizeof(f_flags));
     }
};

/*! \brief The abstract base class for an entry in a directory with lazily filled metadata.

Note that `directory_entry_hash` will hash one of these for you, and a `std::hash<directory_entry>` specialisation
is defined for you so you ought to be able to use directory_entry directly in an `unordered_map<>`.

See `__afio_stat_t__` for explanations of the fields.

\qbk{
[include generated/struct_directory_entry_hash.qbk]
}
*/
class BOOST_AFIO_DECL directory_entry
{
    friend class detail::async_file_io_dispatcher_compat;
    friend class detail::async_file_io_dispatcher_windows;
    friend class detail::async_file_io_dispatcher_linux;
    friend class detail::async_file_io_dispatcher_qnx;

    path::string_type leafname;
    stat_t stat;
    metadata_flags have_metadata;
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void _int_fetch(metadata_flags wanted, handle_ptr dirh);
public:
    //! \constr
    directory_entry() : stat(nullptr), have_metadata(metadata_flags::None) { }
    //! \constr
    directory_entry(path::string_type _leafname, stat_t __stat, metadata_flags _have_metadata) : leafname(_leafname), stat(__stat), have_metadata(_have_metadata) { }
    directory_entry(const directory_entry &) = default;
    directory_entry &operator=(const directory_entry &) = default;
    directory_entry(directory_entry &&o) noexcept : leafname(std::move(o.leafname)), stat(std::move(o.stat)), have_metadata(std::move(o.have_metadata)) { }
    directory_entry &operator=(directory_entry &&o) noexcept
    {
        leafname=std::move(o.leafname);
        stat=std::move(o.stat);
        have_metadata=std::move(o.have_metadata);
        return *this;
    }

    bool operator==(const directory_entry& rhs) const noexcept { return leafname == rhs.leafname; }
    bool operator!=(const directory_entry& rhs) const noexcept { return leafname != rhs.leafname; }
    bool operator< (const directory_entry& rhs) const noexcept { return leafname < rhs.leafname; }
    bool operator<=(const directory_entry& rhs) const noexcept { return leafname <= rhs.leafname; }
    bool operator> (const directory_entry& rhs) const noexcept { return leafname > rhs.leafname; }
    bool operator>=(const directory_entry& rhs) const noexcept { return leafname >= rhs.leafname; }
    //! \return The name of the directory entry. May be empty if the file is deleted.
    path::string_type name() const noexcept { return leafname; }
    //! \return A bitfield of what metadata is ready right now
    metadata_flags metadata_ready() const noexcept { return have_metadata; }
    /*! \brief Fetches the specified metadata, returning that newly available. This is a blocking call if wanted metadata is not yet ready.
    Note that if the call blocks and the leafname no longer exists or the directory handle is null, an exception is thrown.
    \return The metadata now available in this directory entry.
    \param dirh An open handle to the entry's containing directory. You can get this from an op ref using dirop.get_handle().
    \param wanted A bitfield of the metadata to fetch. This does not replace existing metadata.
    */
    metadata_flags fetch_metadata(handle_ptr dirh, metadata_flags wanted)
    {
        metadata_flags tofetch;
        wanted=wanted&metadata_supported();
        tofetch=wanted&~have_metadata;
        if(!!tofetch) _int_fetch(tofetch, dirh);
        return have_metadata;
    }
    /*! \brief Returns a copy of the internal `stat_t` structure. This is a blocking call if wanted metadata is not yet ready.
    Note that if the call blocks and the leafname no longer exists or the directory handle is null, an exception is thrown.
    \return A copy of the internal `stat_t` structure.
    \param dirh An open handle to the entry's containing directory. You can get this from an op ref using dirop.get_handle().
    \param wanted A bitfield of the metadata to fetch. This does not replace existing metadata.
    */
    stat_t fetch_lstat(handle_ptr dirh, metadata_flags wanted=directory_entry::metadata_fastpath())
    {
        fetch_metadata(dirh, wanted);
        return stat;
    }
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#define BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(field) \
decltype(stat_t().st_##field) st_##field() const { if(!(have_metadata&metadata_flags::field)) { BOOST_AFIO_THROW(std::runtime_error("Field st_" #field " not present.")); } return stat.st_##field; } \
decltype(stat_t().st_##field) st_##field(handle_ptr dirh) { if(!(have_metadata&metadata_flags::field)) { _int_fetch(metadata_flags::field, dirh); } return stat.st_##field; }
#else
#define BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(field) \
fieldtype st_##field(handle_ptr dirh=handle_ptr()) { if(!(have_metadata&metadata_flags::field)) { _int_fetch(metadata_flags::field, dirh); } return stat.st_##field; }
#endif
#ifndef WIN32
    //! Returns st_dev \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(dev)
#endif
    //! Returns st_ino \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(ino)
    //! Returns st_type \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(type)
#ifndef WIN32
    //! Returns st_perms \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(perms)
#endif
    //! Returns st_nlink \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(nlink)
#ifndef WIN32
    //! Returns st_uid \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(uid)
    //! Returns st_gid \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(gid)
    //! Returns st_rdev \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(rdev)
#endif
    //! Returns st_atim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(atim)
    //! Returns st_mtim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(mtim)
    //! Returns st_ctim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(ctim)
    //! Returns st_size \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(size)
    //! Returns st_allocated \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(allocated)
    //! Returns st_blocks \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(blocks)
    //! Returns st_blksize \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(blksize)
    //! Returns st_flags \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(flags)
    //! Returns st_gen \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(gen)
    //! Returns st_birthtim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
    BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(birthtim)

    //! A bitfield of what metadata is available on this platform. This doesn't mean all is available for every filing system.
    static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC metadata_flags metadata_supported() noexcept;
    //! A bitfield of what metadata is fast on this platform. This doesn't mean all is available for every filing system.
    static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC metadata_flags metadata_fastpath() noexcept;
    //! The maximum number of entries which is "usual" to fetch at once i.e. what your libc does.
    static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t compatibility_maximum() noexcept;
};

/*! \brief A hasher for directory_entry, hashing inode and birth time (if available on this platform).
*/
struct directory_entry_hash
{
public:
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4310) // cast truncates constant value
#endif
    size_t operator()(const directory_entry &p) const
    {
        size_t seed = (size_t) 0x9ddfea08eb382d69ULL;
        detail::hash_combine(seed, p.st_ino());
        if(!!(directory_entry::metadata_supported() & metadata_flags::birthtim))
            detail::hash_combine(seed, p.st_birthtim().time_since_epoch().count());
        return seed;
    }
#ifdef _MSC_VER
#pragma warning(pop)
#endif
};

/*! \brief The abstract base class encapsulating a platform-specific file handle

Note that failure to explicitly schedule closing a file handle in the dispatcher means it will be synchronously closed on last reference count
by handle. This can consume considerable time, especially if SyncOnClose is enabled.

\qbk{
[include generated/struct_handle_1_1mapped_file.qbk]
[include generated/group_async_io_handle__ops.qbk]
}
*/
class handle : public std::enable_shared_from_this<handle>
{
    friend class dispatcher;
    friend struct detail::async_io_handle_posix;
    friend struct detail::async_io_handle_windows;
    friend class detail::async_file_io_dispatcher_compat;
    friend class detail::async_file_io_dispatcher_windows;
    friend class detail::async_file_io_dispatcher_linux;
    friend class detail::async_file_io_dispatcher_qnx;

    dispatcher *_parent;
    chrono::system_clock::time_point _opened;
    file_flags _flags;
protected:
    handle_ptr dirh;
    atomic<off_t> bytesread, byteswritten, byteswrittenatlastfsync;
    handle(dispatcher *parent, file_flags flags) : _parent(parent), _opened(chrono::system_clock::now()), _flags(flags), bytesread(0), byteswritten(0), byteswrittenatlastfsync(0) { }
    //! Calling this directly can cause misoperation. Best to avoid unless you have inspected the source code for the consequences.
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void close() BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
public:
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~handle() { }
    //! Returns the parent of this io handle
    dispatcher *parent() const { return _parent; }
    //! Returns a handle to the directory containing this handle. Only works if `file_flags::hold_parent_open` was specified when this handle was opened.
    handle_ptr container() const { return dirh; }
    //! In which way this handle is opened or not
    enum class open_states
    {
      closed, //!< This handle is closed.
      open,   //!< This handle is open as a normal handle.
      opendir //!< This handle is open as a cached directory handle, and therefore closing it explicitly has no effect.
    };
    //! Returns if this handle is opened or not
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC open_states is_open() const BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    //! Returns the native handle of this io handle. On POSIX, you can cast this to a fd using `(int)(size_t) native_handle()`. On Windows it's a simple `(HANDLE) native_handle()`.
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void *native_handle() const BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    //! Returns when this handle was opened
    const chrono::system_clock::time_point &opened() const { return _opened; }
    /*! \brief Returns the path of this i/o handle right now if the handle is open and \em refresh is true, else last known good. May be null if the file has been deleted.
    
    Note the refreshed path completely dereferences any intermediate symbolic links to return a truly absolute canonical path, and therefore may look quite different to before.
    Some operating systems unfortunately also return any one of the hard links to the file, so if hard links is greater than one the path refreshed will randomly permute.
    
    \ntkernelnamespacenote
    \return The path of this i/o handle right now.
    \param refresh Whether to ask the OS for the current path of this handle.
    \ingroup async_io_handle__ops
    \raceguarantees{
    [raceguarantee FreeBSD..Paths are only refreshed for directories, not files.]
    [raceguarantee Linux, Windows..Paths are always refreshed and ignore other hard links.]
    [raceguarantee OS X..Paths are only refreshed for directories and files with a single hard link.]
    }
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path(bool refresh=false) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    //! Returns the last known good path of this i/o handle. May be null if the file has been deleted.
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path() const BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    //! Returns the final flags used when this handle was opened
    file_flags flags() const { return _flags; }
    //! True if this handle was opened as a file
    bool opened_as_file() const { return !(_flags&file_flags::int_opening_dir) && !(_flags&file_flags::int_opening_link); }
    //! True if this handle was opened as a directory
    bool opened_as_dir() const { return !!(_flags&file_flags::int_opening_dir); }
    //! True if this handle was opened as a symlink
    bool opened_as_symlink() const { return !!(_flags&file_flags::int_opening_link); }
    //! True if this handle is used by the directory handle cache (not UniqueDirectoryHandle and is open for write and not open for write)
    bool available_to_directory_cache() const { return opened_as_dir() && !(_flags&file_flags::unique_directory_handle) && !!(_flags&file_flags::read) && !(_flags&file_flags::write); }
    //! Returns how many bytes have been read since this handle was opened.
    off_t read_count() const { return bytesread; }
    //! Returns how many bytes have been written since this handle was opened.
    off_t write_count() const { return byteswritten; }
    //! Returns how many bytes have been written since this handle was last fsynced.
    off_t write_count_since_fsync() const { return byteswritten-byteswrittenatlastfsync; }
    /*! \brief Returns a mostly filled directory_entry for the file or directory referenced by this handle. Use `metadata_flags::All` if you want it as complete as your platform allows, even at the cost of severe performance loss.

    Related types: `__afio_directory_entry__`, `__afio_stat_t__`
    \return A directory entry for this handle.
    \param wanted The metadata wanted.
    \ingroup async_io_handle__ops
    \raceguarantees{
    [raceguarantee FreeBSD..Race free if handle open for directories and regular files only, else if handle closed or a symlink race free up to the containing directory. All metadata is fetched in a single shot.]
    [raceguarantee Linux..Race free if handle open, else if handle closed race free up to the containing directory. All metadata is fetched in a single shot.]
    [raceguarantee OS X..Race free if handle open for directories and regular files only. No guarantees if handle closed or a symlink.]
    [raceguarantee Windows..Handle must be open and is always race free. Metadata may be fetched in a single shot if at least two categories requested, or else the following categories apply: (i) ino (ii) type, atim, mtim, ctim, birthtim, sparse, compressed (iii) nlink, size, allocated, blocks.]
    }
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC directory_entry direntry(metadata_flags wanted=directory_entry::metadata_fastpath()) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Returns a mostly filled stat_t structure for the file or directory referenced by this handle. Use `metadata_flags::All` if you want it as complete as your platform allows, even at the cost of severe performance loss. Calls direntry(), so same race guarantees as that call.

    Related types: `__afio_directory_entry__`, `__afio_stat_t__`
    */
    stat_t lstat(metadata_flags wanted=directory_entry::metadata_fastpath())
    {
        directory_entry de(direntry(wanted));
        return de.fetch_lstat(handle_ptr() /* actually unneeded */, wanted);
    }
    /*! \brief Returns the target path of this handle if it is a symbolic link.

    \ntkernelnamespacenote
    \return The path the symbolic link points to. May not exist or even be valid.
    \ingroup async_io_handle__ops
    \raceguarantees{
    [raceguarantee FreeBSD..Race free up to the containing directory.]
    [raceguarantee Linux, Windows..Race free if handle open, else up to the containing directory.]
    [raceguarantee OS X..No guarantees.]
    }
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path target() BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    //! A holder of a mapped file.
    struct BOOST_AFIO_DECL mapped_file
    {
      friend class handle;
      handle_ptr h;   //!< The file being mapped
      void *addr;     //!< The address in memory of the map
      size_t length;  //!< The length of the map
      off_t offset;   //!< The offset of the map into the file
      mapped_file(const mapped_file &) = delete;
      mapped_file(mapped_file &&) = delete;
      mapped_file &operator=(const mapped_file &) = delete;
      mapped_file &operator=(mapped_file &&) = delete;
      mapped_file(handle_ptr _h, void *_addr, size_t _length, off_t _offset) : h(std::move(_h)), addr(_addr), length(_length), offset(_offset) { }
      ~mapped_file();
    };
    //! A type alias to a mapped file pointer
    using mapped_file_ptr = std::unique_ptr<mapped_file>;
    //! Maps the file into memory, returning a null pointer if couldn't map (e.g. address space exhaustion). Do NOT mix this with `file_flags::os_direct`!
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC mapped_file_ptr map_file(size_t length = (size_t)-1, off_t offset = 0, bool read_only = false) { return nullptr; }
    /*! \brief Hard links the file to a new location on the same volume.

    If you wish to make a temporary file whose contents are ready appear at a location and error out if
    a file entry is already there, use link() and if success, unlink() on the former location. If you wish
    to always overwrite the destination, use atomic_relink() instead.    

    On Windows, the destination directory cannot have any handle opened to it with delete/rename privileges (`file_flags::write`)
    anywhere in the system. This is an operating system limitation.
 
    \ntkernelnamespacenote

    Related types: `__afio_path_req__`

    \param req The absolute or relative (in which case precondition specifies a directory) path to create a hard link at.
    \ingroup async_io_handle__ops
    \raceguarantees{
    [raceguarantee FreeBSD..Race free up to the containing directory for both source and target.]
    [raceguarantee Linux, Windows..Race free for source if handle open, else up to the containing directory. Race free up to the target directory.]
    [raceguarantee OS X..No guarantees.]
    }
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void link(const path_req &req) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Unlinks the file from its present location as determined by path(true), which could be any hard link on
    those operating systems with an unstable path(true). Other links may remain to the same file.
    
    On Microsoft Windows, this routine unlinks items as follows:
    
    1. It tries to atomically rename the item to the root of the mounted volume it lives in with a .afiodXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX where
    the X's are a 128 bit crypto random hexadecimal. If that fails, it tries the next directory up, and
    the next after that until success if any. This rename may fail for any reason, including if it is a directory with
    open file handles somewhere within. If it fails, the rename is skipped.
    
    2. It marks the item with hidden and system attributes to hide it from normal directory enumeration.
    
    3. It sets the delete on last handle close flag. At some stl_future point Windows will delete the item, until which it will hang
    around in a zombie state with an unknowable name and location unopenable by any new processes.
    
    The reason for such complexity is that this algorithm, if it renames successfully, neatly works around a number of
    annoying features in Windows, specifically that when you delete a file you actually don't delete it till an unknown amount
    of time later. This breaks code which tries to delete a directory tree, and finds that the directories won't delete because
    they still contain files supposedly deleted but actually not quite yet. By renaming the items as far away as possible, this
    problem ought to go away - unless of course that the user does not have permissions to write into any directory other than the
    one being eventually deleted, in which case you will still see the strange access denied and directory not empty errors from
    before.

    \ntkernelnamespacenote

    Related types: `__afio_path_req__`

    \ingroup async_io_handle__ops
    \raceguarantees{
    [raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
    [raceguarantee Windows..Race free if handle open, else up to the containing directory.]
    [raceguarantee OS X..No guarantees.]
    }
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlink() BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Links the file to a new location and unlinks the file from its present location as determined by path(true),
    <em>atomically overwriting any file entry at the new location</em>. Very useful for preparing file content elsewhere and once ready, atomically
    making it visible at some named location to other processes. Note that operating systems with an unstable path(true) may
    relink any hard link to the file to the new location.

    Note that not all filing systems guarantee the atomicity of the relink itself (i.e. the file may appear at two locations
    in the filing system for a period of time), though all supported platforms do
    guarantee the atomicity of the replaced location i.e. the location you are relinking to will always refer to
    some valid file to all readers, and will never be deleted or missing. Some filing systems may also fail to do the unlink
    if power is lost close to the relinking operation.
    
    On Windows, the destination directory cannot have any handle opened to it with delete/rename privileges (`file_flags::write`)
    anywhere in the system. This is an operating system limitation.

    \ntkernelnamespacenote

    Related types: `__afio_path_req__`

    \param req The absolute or relative (in which case precondition specifies a directory) path to relink to.
    \ingroup async_io_handle__ops
    \raceguarantees{
    [raceguarantee FreeBSD, Linux..Race free up to the containing directory for both source and target.]
    [raceguarantee OS X..No guarantees.]
    [raceguarantee Windows..Race free for source if handle open, else up to the containing directory. Race free up to the target directory.]
    }
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void atomic_relink(const path_req &req) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC

#if 0
    // Undocumented deliberately
    enum class change_flags : size_t
    {
      created=(1<<0),    // NOTE_EXTEND,                       IN_CREATE, 
      renamed=(1<<1),    // NOTE_RENAME,       IN_MOVED_FROM/IN_MOVED_TO, 
      deleted=(1<<2),    // NOTE_DELETE,                       IN_DELETE, 
      attributes=(1<<3), // NOTE_ATTRIB,                       IN_ATTRIB, 
      opened=(1<<4),     //           ?,                         IN_OPEN, 
      closed=(1<<5),     //           ?, IN_CLOSE_WRITE/IN_CLOSE_NOWRITE, 
      read=(1<<6),       //           ?,                       IN_ACCESS, 
      written=(1<<7),    //  NOTE_WRITE,                       IN_MODIFY, 
      extended=(1<<8),   // NOTE_EXTEND,                               ?, 
      
      region_locked=(1<<16),
      region_timedout=(1<<17),
      region_unlocked=(1<<18)
    };
    // Undocumented deliberately
    struct change_listener : public std::enable_shared_from_this<change_listener>
    {
      virtual ~change_listener() { }
      virtual void operator()(handle *h, change_flags changed, void *additional)=0;
    };
    // Undocumented deliberately
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void listen(const std::vector<std::pair<change_flags>, std::shared_ptr<change_listener>> &listeners) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    // Undocumented deliberately
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlisten(const std::vector<std::pair<change_flags>, std::shared_ptr<change_listener>> &listeners) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
#endif
};

/*! \brief Retrieves the currently set async_file_io_dispatcher for this thread, optionally setting it to
a new dispatcher.

\return The current async_file_io_dispatcher.
\param new_dispatcher The new async_file_io_dispatcher to set.
\ingroup async_file_io_dispatcher
*/
BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC dispatcher_ptr current_dispatcher(option<dispatcher_ptr> new_dispatcher = empty);

/*! \class current_dispatcher_guard

RAII holder for the current async file i/o dispatcher.

\ingroup async_file_io_dispatcher
*/
class current_dispatcher_guard
{
  dispatcher_ptr _old;
public:
  current_dispatcher_guard(dispatcher_ptr _new) : _old(current_dispatcher(_new)) { }
  ~current_dispatcher_guard() { current_dispatcher(_old); }
  //! Restore the former async file i/o dispatcher now.
  void release() { current_dispatcher(_old); _old.reset(); }
  //! Don't restore the former async file i/o dispatcher.
  void dismiss() { _old.reset(); }
  //! Set a different former async file i/o dispatcher on destruction.
  void reset(dispatcher_ptr p) { _old=p; }
};

//! Trait for determining if a type is an afio::future<T>
template<class T> struct is_future : std::false_type { };
template<class T> struct is_future<future<T>> : std::true_type {};

// Temporary friends for future<>
namespace detail
{
  struct barrier_count_completed_state;
  template<bool rethrow, class Iterator> inline stl_future<std::vector<handle_ptr>> when_all_ops(Iterator first, Iterator last);
  template<bool rethrow, class Iterator> inline stl_future<handle_ptr> when_any_ops(Iterator first, Iterator last);

  // Shim code for lightweight future continuations
  template<class R, bool return_is_lightweight_future=is_lightweight_future<R>::value, bool return_is_afio_future=is_future<R>::value> struct continuation_return_type { using future_type = future<R>; using promise_type = void; };
  template<class R, bool _> struct continuation_return_type<R, true, _> { using future_type = R; using promise_type = typename future_type::promise_type; };
  template<class R, bool _> struct continuation_return_type<R, _, true> { using future_type = R; using promise_type = void; };
  template<class future_type, class promise_type> struct do_continuation;
}

/*! \ref future
*/
template<> class future<void>
{
    // Temporary friends until lightweight future promise comes in
    friend struct detail::barrier_count_completed_state;
    template<bool rethrow, class Iterator> friend inline stl_future<std::vector<handle_ptr>> detail::when_all_ops(Iterator first, Iterator last);
    template<bool rethrow, class Iterator> friend inline stl_future<handle_ptr> detail::when_any_ops(Iterator first, Iterator last);

    dispatcher *_parent;              //!< The parent dispatcher
    size_t _id;                                          //!< A unique id for this operation
    shared_future<handle_ptr> _h;  //!< A stl_future handle to the item being operated upon
public:
    future(future<void> &&o, stl_future<void> &&result) : future<void>(std::move(o)) { }
    // NOTE TO SELF: MAKE THE CONSTRUCTORS AND MEMBER FUNCTIONS constexpr WHEN I MERGE LIGHTWEIGHT FUTURE-PROMISES

    //! The type of value potentially returned
    using value_type = void;
    //! The error type potentially returned
    using error_type = error_code;
    //! The exception type potentially returned
    using exception_type = exception_ptr;

    //! \constr
    future() : _parent(nullptr), _id(0) { }
    //! \cconstr
    future(const future &o) = default;
    //! \mconstr
    future(future &&o) = default;
    /*! Constructs an instance.
    \param parent The dispatcher this op belongs to.
    \param id The unique non-zero id of this op.
    \param handle A shared_ptr to shared state between all instances of this reference.
    \param check_handle Whether to have validation additionally check if a handle is not null
    \param validate Whether to check the inputs and shared state for valid (and not errored) values
    */
    future(dispatcher *parent, size_t id, shared_future<handle_ptr> handle, bool check_handle=true, bool validate=true) : _parent(parent), _id(id), _h(std::move(handle)) { if(validate) _validate(check_handle); }
    /*! Constructs an instance.
    \param _handle A shared_ptr to shared state between all instances of this reference.
    \param check_handle Whether to have validation additionally check if a handle is not null
    \param validate Whether to check the inputs and shared state for valid (and not errored) values
    */
    future(handle_ptr _handle, bool check_handle=true, bool validate=true) : _parent(_handle->parent()), _id((size_t)-1) { promise<handle_ptr> p; p.set_value(std::move(_handle)); _h=p.get_future(); if(validate) _validate(check_handle); }
    /*! Constructs an instance.
    \param parent The dispatcher this op belongs to.
    \param id The unique non-zero id of this op.
    */
    future(dispatcher *parent, size_t id) : _parent(parent), _id(id) { }
    //! \cassign
    future &operator=(const future &o) { _parent = o._parent; _id = o._id; _h = o._h; return *this; }
    //! \massign
    future &operator=(future &&o) noexcept { _parent = std::move(o._parent); _id = std::move(o._id); _h = std::move(o._h); return *this; }

    //! True if this future is valid
    bool valid() const noexcept { return _parent && _id; }
    //! \brief Same as `true_(tribool(*this))`
    explicit operator bool() const noexcept { return has_value(); }
    //! \brief True if monad is not empty
    bool is_ready() const noexcept
    {
      return valid() || _h.wait_for(chrono::seconds(0)) == future_status::ready;
    }
    //! \brief True if monad contains a value_type
    bool has_value() const noexcept { return is_ready() && !has_exception(); }
    //! \brief True if monad contains an error_type
    bool has_error() const noexcept
    {
      if (!is_ready())
        return false;
      error_type ec = get_error();
      return ec && ec.category() != monad_category();
    }
    /*! \brief True if monad contains an exception_type or error_type (any error_type is returned as an exception_ptr by get_exception()).
    This needs to be true for both for compatibility with Boost.Thread's future. If you really want to test only for has exception only,
    pass true as the argument.
    */
    bool has_exception(bool only_exception = false) const noexcept
    {
      if (!is_ready())
        return false;
      return !!get_exception();
    }

    //! The parent dispatcher of this future
    dispatcher *parent() const noexcept { return _parent; }
    //! \deprecate{Expected to be removed in the v1.5 engine}
    size_t id() const noexcept { return _id; }
    //! Retrieves the handle or exception from the shared state, rethrowing any exception. Returns a null shared pointer if this future is invalid.
    handle_ptr get_handle(bool return_null_if_errored=false) const
    {
        if(!_parent && !_id)
            return handle_ptr();
        // std::shared_future in older libstdc++ does not have a const get().
        if(!return_null_if_errored)
            return const_cast<future *>(this)->_h.get();
        auto e=get_exception_ptr(_h);
        return e ? handle_ptr() : const_cast<future *>(this)->_h.get();
    }
    //! Retrieves the handle or exception from the shared state, rethrowing any exception but setting _ec if there is an error. Returns a null shared pointer if this future is invalid.
    handle_ptr get_handle(error_type &ec) const
    {
      if (!_parent && !_id)
        return handle_ptr();
      ec = get_error();
      return ec ? handle_ptr() : const_cast<future *>(this)->_h.get();
    }
    //! Dereferences the handle from the shared state. Same as *h.get_handle().
    const handle &operator *() const { return *get_handle(); }
    //! Dereferences the handle from the shared state. Same as *h.get_handle().
    handle &operator *() { return *get_handle(); }
    //! Dereferences the handle from the shared state. Same as h.get_handle()->get().
    const handle *operator->() const { return get_handle().get(); }
    //! Dereferences the handle from the shared state. Same as h.get_handle()->get().
    handle *operator->() { return get_handle().get(); }
    //! Waits for the future to become ready, rethrowing any exception found. Throws a `future_errc::no_state` if this future is invalid.
    void get()
    {
      if (!valid())
        throw future_error(future_errc::no_state);
      _h.get();
    }
    //! Waits for the future to become ready, returning any error state found
    error_type get_error() const
    {
      if (!valid())
        throw future_error(future_errc::no_state);
      auto e = get_exception_ptr(_h);
      if (e)
      {
        try
        {
          rethrow_exception(e);
        }
        catch (const system_error &_e)
        {
          return _e.code();
        }
        catch (...)
        {
          return error_type((int)monad_errc::exception_present, monad_category());
        }
      }
      return error_type();
    }
    //! Waits for the future to become ready, returning any error state found
    exception_type get_exception() const
    {
      if (!valid())
        throw future_error(future_errc::no_state);
      return get_exception_ptr(_h);
    }
    //! Waits for the future to become ready. Throws a `future_errc::no_state` if this future is invalid.
    void wait() const
    {
      if (!valid())
        throw future_error(future_errc::no_state);
      _h.wait();
    }
    //! Waits for the future to become ready for a period. Throws a `future_errc::no_state` if this future is invalid.
    template<class Rep, class Period> future_status wait_for(const chrono::duration<Rep, Period> &duration) const
    {
      if (!valid())
        throw future_error(future_errc::no_state);
      return _h.wait_for(duration);
    }
    //! Waits for the future to become ready until a deadline. Throws a `future_errc::no_state` if this future is invalid.
    template<class Clock, class Duration> future_status wait_until(const chrono::time_point<Clock, Duration> &deadline) const
    {
      if (!valid())
        throw future_error(future_errc::no_state);
      return _h.wait_until(deadline);
    }
    //! Schedules a callable to be invoked after this future becomes ready. If this future is null, use the current async file i/o dispatcher.
    template<class U> auto then(U &&f) -> typename detail::continuation_return_type<decltype(f(*this))>::future_type
    {
      using future_type = typename detail::continuation_return_type<decltype(f(*this))>::future_type;
      using promise_type = typename detail::continuation_return_type<decltype(f(*this))>::promise_type;
      return detail::do_continuation<future_type, promise_type>()(parent(), this, std::forward<U>(f));
    }
    //! Validates contents
    bool validate(bool check_handle=true) const
    {
        if(!valid()) return false;
        // If h is valid and ready and contains an exception, throw it now
        if(_h.valid() && BOOST_AFIO_V2_NAMESPACE::is_ready(_h))
        {
            if(check_handle)
                if(!const_cast<shared_future<handle_ptr> &>(_h).get().get())
                    return false;
        }
        return true;
    }
protected:
    void _validate(bool check_handle=true) const
    {
#if BOOST_AFIO_VALIDATE_INPUTS
        if(!validate(check_handle))
            BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
    }
};
/*! \class future
\tparam T Any returned result. Note this is defaulted to `void` for you, so usually you write `future<>`.
\brief The future status of a scheduled asynchronous operation

As of v1.4 of the AFIO engine, the legacy `async_io_op` struct has been replaced with this custom future type
based on the lightweight future-promise factory toolkit in forthcoming Boost.Monad. This custom future type
consists of two pieces of future data each with different semantics:

1. A `handle_ptr` retrieved using `get_handle()`, `operator*` and `operator->` - this is
the shared i/o handle returned by the asynchronous operation. This has non-consuming semantics i.e. you can call
`get_handle()` as many times as you like. Note that for legacy compatibility reasons, calling `get_handle()` on
an invalid instance returns a null shared pointer instead of an exception throw.

2. If T is non-void, any type `T` - this is any additional data returned by an asynchronous operation above and beyond the i/o handle
(e.g. `enumerate()` returns a `std::pair<std::vector<directory_entry>, bool>`. This has *consuming* semantics, so
calling `get()` returns the result exactly once.

The reason for the difference in semantics is because it is very common that you need access to an earlier i/o handle
in a sequence if some operation returns an error, and besides the shared pointer encapsulation makes non-consumption
cost free.

Other than the fact that `get()` returns a `T` and `get_handle()` returns a handle, the errored and excepted state
for both is identical and non-consuming for both.

Finally, note that there is a freely available type slice from `future<T>` to `future<void>` which moves/copies only
the `future<void>` part of the `future<T>`, leaving the `T` behind. This is because the AFIO engine resides behind a stable
ABI layer which cannot know anything about arbitrary types, and therefore it accepts only `future<void>`. Equally, this
means you can supply a `future<T>` as a precondition to another op safe in the knowledge that any `T` part will remain
untouched for later consumption.
*/
template<class T> class future : public future<void>
{
  stl_future<T> _result;
public:
  //! The type of value potentially returned
  using value_type = T;

  future() = default;
  /*! Constructs an instance.
  \param parent The dispatcher this op belongs to.
  \param id The unique non-zero id of this op.
  \param handle A shared_future to shared state between all instances of this reference.
  \param result A future to any result from the operation.
  \param check_handle Whether to have validation additionally check if a handle is not null
  \param validate Whether to check the inputs and shared state for valid (and not errored) values
  */
  future(dispatcher *parent, size_t id, shared_future<handle_ptr> handle, stl_future<T> result, bool check_handle = true, bool validate = true) : future<void>(parent, id, std::move(handle), check_handle, validate), _result(std::move(result)) { }
  /*! Constructs an instance from an existing future<void>
  \param o The future<void>
  \param result The future<T> to add
  */
  future(future<void> &&o, stl_future<T> &&result) : future<void>(std::move(o)), _result(std::move(result)) { }
  //! True if this future is valid
  bool valid(bool just_handle=false) const noexcept { return future<void>::valid() && (just_handle || _result.valid()); }
  //! Waits for the future to become ready, returning any value or rethrowing any exception found. Throws a `future_errc::no_state` if this future is invalid.
  T get()
  {
    return _result.get();
  }
  //! Schedules a callable to be invoked after this future becomes ready. If this future is null, use the current async file i/o dispatcher.
  template<class U> auto then(U &&f) -> typename detail::continuation_return_type<decltype(f(*this))>::future_type
  {
    using future_type = typename detail::continuation_return_type<decltype(f(*this))>::future_type;
    using promise_type = typename detail::continuation_return_type<decltype(f(*this))>::promise_type;
    return detail::do_continuation<future_type, promise_type>()(parent(), this, std::forward<U>(f));
  }
};

namespace detail
{
  // For continuations returning lightweight futures
  template<class future_type, class promise_type> struct do_continuation
  {
    template<class D, class U> future_type operator()(D *d, future<> *src, U &&f)
    {
      if (!d) d = current_dispatcher().get();
      auto p = std::make_shared<promise_type>();
      d->completion(*src, std::make_pair(async_op_flags::immediate, [f, p](size_t id, future<> _f) -> std::pair<bool, handle_ptr> {
        try
        {
          using result_type = decltype(f(_f));
          f(_f).then([p](const result_type &_f) {
            auto s(_f.get_state());
            try
            {
              p->set_state(std::move(s));
            }
            catch (...) { /* Really should filter for no_state but this is shim code */ }
          });
        }
        catch (...)
        {
          p->set_exception(current_exception());
        }
        return std::make_pair(true, _f.get_handle());
      }));
      return p->get_future();
    }
  };
  // For continuations returning shim AFIO futures or some naked type
  template<class R> struct do_continuation<future<R>, void>
  {
    template<class D, class T, class U> future<R> operator()(D *d, future<T> *src, U &&f)
    {
      if (!d) d = current_dispatcher().get();
      // TEMPORARY: For continuations taking a future<T> where T is not void
      // we have no way of passing the correct future to the continuation until
      // the lightweight future promise refactor
      //
      // So simply call the continuation now. When it calls .get() it will block.
      return f(*src);
    }
    template<class D, class U> future<> operator()(D *d, future<> *src, U &&f)
    {
      if (!d) d = current_dispatcher().get();
      return d->completion(*src, std::make_pair(async_op_flags::immediate, [f](size_t id, future<> _f) -> std::pair<bool, handle_ptr> {
        f(_f);
        return std::make_pair(true, _f.get_handle());
      }));
    }
  };
}

// This is a result_of filter to work around the weird mix of brittle decltype(), SFINAE incapable
// std::result_of and variadic template overload resolution rules in VS2013. Works on other compilers
// too of course, it simply prefilters out the call() overloads not matching the variadic overload.
namespace detail
{
#if 0
    template<class C, class... Args> struct vs2013_variadic_overload_resolution_workaround;
    // Match callable
    template<class R, class... OArgs, class... Args> struct vs2013_variadic_overload_resolution_workaround<R (*)(OArgs...), Args...>
    {
        typedef typename std::result_of<R(*)(Args...)>::type type;
    };
    // Match callable
    template<class R, class T, class... OArgs, class... Args> struct vs2013_variadic_overload_resolution_workaround<R (T::*)(OArgs...) const, Args...>
    {
        typedef typename std::result_of<R (T::*)(Args...) const>::type type;
    };
    // Match callable
    template<class R, class T, class... OArgs, class... Args> struct vs2013_variadic_overload_resolution_workaround<R (T::*const)(OArgs...) const, Args...>
    {
        typedef typename std::result_of<R (T::*const)(Args...) const>::type type;
    };
#else
    /*
    call(const std::vector<future> &ops             , const std::vector<std::function<R()>> &callables              );
    call(const std::vector<std::function<R()>> &callables                                                                );
    call(const future &req                          , std::function<R()> callback                                   );
    call(const future &req                          , C callback                                      , Args... args);
    */
    template<class C, class... Args> struct vs2013_variadic_overload_resolution_workaround
    {
        typedef typename std::result_of<C(Args...)>::type type;
    };
    // Disable C being a const std::vector<std::function<R()>> &callables
    template<class T, class... Args> struct vs2013_variadic_overload_resolution_workaround<std::vector<T>, Args...>;
#endif
    template<class Impl, class Handle> handle_ptr decode_relative_path(path_req &req, bool force_absolute=false);
}

/*! \class dispatcher
\brief Abstract base class for dispatching file i/o asynchronously

This is a reference counted instance with platform-specific implementation in object code.
Construct an instance using the `boost::afio::make_dispatcher()` function.

\qbk{
[/ link afio.reference.functions.async_file_io_dispatcher `async_file_io_dispatcher()`]
[/ include generated/group_dispatcher__filter.qbk]
[/ include generated/group_dispatcher__completion.qbk]
[/ include generated/group_dispatcher__call.qbk]
[include generated/group_dispatcher__filedirops.qbk]
[include generated/group_dispatcher__enumerate.qbk]
[include generated/group_dispatcher__extents.qbk]
[include generated/group_dispatcher__statfs.qbk]
[/ include generated/group_dispatcher__depends.qbk]
[/ include generated/group_dispatcher__barrier.qbk]
[include generated/group_dispatcher__misc.qbk]
}
*/
class BOOST_AFIO_DECL dispatcher : public std::enable_shared_from_this<dispatcher>
{
    //friend BOOST_AFIO_DECL dispatcher_ptr async_file_io_dispatcher(thread_source &threadpool=process_threadpool(), file_flags flagsforce=file_flags::none, file_flags flagsmask=file_flags::none);
    template<class Impl, class Handle> friend handle_ptr detail::decode_relative_path(path_req &req, bool force_absolute);
    friend struct detail::async_io_handle_posix;
    friend struct detail::async_io_handle_windows;
    friend class detail::async_file_io_dispatcher_compat;
    friend class detail::async_file_io_dispatcher_windows;
    friend class detail::async_file_io_dispatcher_linux;
    friend class detail::async_file_io_dispatcher_qnx;

    detail::dispatcher_p *p;
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_directory_cached_handle_path_changed(path oldpath, path newpath, handle_ptr h);
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_add_io_handle(void *key, handle_ptr h);
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_del_io_handle(void *key);
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> int_op_from_scheduled_id(size_t id) const;

protected:
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher(std::shared_ptr<thread_source> threadpool, file_flags flagsforce, file_flags flagsmask);
    std::pair<bool, handle_ptr> doadopt(size_t, future<>, handle_ptr h)
    {
        return std::make_pair(true, h);
    }
public:
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void testing_flags(detail::unit_testing_flags flags);
    //! Destroys the dispatcher, blocking inefficiently if any ops are still in flight.
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~dispatcher();

    //! Returns the thread source used by this dispatcher
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::shared_ptr<thread_source> threadsource() const;
    //! Returns file flags as would be used after forcing and masking bits passed during construction
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC file_flags fileflags(file_flags flags) const;
    //! Returns the current wait queue depth of this dispatcher
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t wait_queue_depth() const;
    //! Returns the number of open items in this dispatcher
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t fd_count() const;
#ifndef DOXYGEN_SHOULD_SKIP_THIS
    /* \brief Returns an op ref for a given \b currently scheduled op id, throwing an exception if id not scheduled at the point of call.
    Can be used to retrieve exception state from some op id, or one's own shared stl_future.
    
    \return An future<> with the same shared stl_future as all op refs with this id.
    \param id The unique integer id for the op.
    */
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> op_from_scheduled_id(size_t id) const;

    // The type of an op filter callback handler \ingroup dispatcher__filter
    typedef void filter_t(detail::OpType, future<> &);
    // The type of a readwrite filter callback handler \ingroup dispatcher__filter
    typedef void filter_readwrite_t(detail::OpType, handle *, const detail::io_req_impl<true> &, off_t, size_t, size_t, const error_code &, size_t);
    /* \brief Clears the post op and readwrite filters. Not threadsafe.

    \ingroup dispatcher__filter
    \complexity{O(1).}
    \qexample{filter_example}
    */
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void post_op_filter_clear();
    /* \brief Install op filters for non-buffer taking ops. Not threadsafe.

    `std::function<dispatcher::filter_t>` will be called after every op of type `detail::OpType`
    completes (`detail::OpType::Unknown` means call this filter for all ops) with the op type and op output.

    Note that filters are currently implemented as a linear scan, so a full iteration of all filters is done
    for every op completed. The filter is called straight after an op's stl_future is set and before any completions
    are issued. Any exceptions thrown by the filter are thrown away.

    \param filters A batch of pairs of op type to be filtered and bound filter handler functions of type `filter_t`
    \ingroup dispatcher__filter
    \complexity{O(N) where N is the total number of filters currently configured.}
    \qexample{filter_example}
    */
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void post_op_filter(std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_t>>> filters);
    /* \brief Install read/write op filters, useful for tight ASIO integration. Not threadsafe.

    `std::function<dispatcher::filter_buffers_t>` will be called after every op of type `detail::OpType`
    completes (`detail::OpType::Unknown` means call this filter for all ops) with the op type, file handle, op input, 
    file offset, buffers offset, buffers amount, error state and bytes transferred. Any filter other than read() and write()
    will be ignored, for those use post_op_filter().

    Note that buffer filters are currently implemented as a linear scan, so a full iteration of all buffer filters is done
    for every read/write op completed. The filter is called straight after a read or write operation has completed, and
    BEFORE any checks that it transferred the data it was supposed to. Any exceptions thrown by the filter are reported
    as if the read/write operation threw them, and filter processing stops at the filter which threw.

    \param filters A batch of pairs of op type to be filtered and bound filter handler functions of type `filter_buffers_t`
    \ingroup dispatcher__filter
    \complexity{O(N) where N is the total number of filters currently configured.}
    \qexample{filter_example}
    */
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void post_readwrite_filter(std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_readwrite_t>>> filters);

    // The type returned by a completion handler \ingroup dispatcher__completion
    typedef std::pair<bool, handle_ptr> completion_returntype;
    // The type of a completion handler \ingroup dispatcher__completion
    typedef completion_returntype completion_t(size_t, future<>);
#ifndef DOXYGEN_SHOULD_SKIP_THIS
#if defined(BOOST_AFIO_ENABLE_BENCHMARKING_COMPLETION) || BOOST_AFIO_HEADERS_ONLY==0 // Only really used for benchmarking
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> completion(const std::vector<future<>> &ops, const std::vector<std::pair<async_op_flags, dispatcher::completion_t *>> &callbacks);
    inline future<> completion(const future<> &req, const std::pair<async_op_flags, dispatcher::completion_t *> &callback);
#endif
#endif
    /* \brief Schedule a batch of asynchronous invocations of the specified functions when their supplied operations complete.

    \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.}
    \return A batch of op handles
    \param ops A batch of precondition op handles.
    \param callbacks A batch of pairs of op flags and bound completion handler functions of type `completion_t`
    \ingroup dispatcher__completion
    \qbk{distinguish, batch bound functions}
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.}
    \exceptionmodelstd
    \qexample{completion_example1}
    */
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> completion(const std::vector<future<>> &ops, const std::vector<std::pair<async_op_flags, std::function<dispatcher::completion_t>>> &callbacks);
    /* \brief Schedule the asynchronous invocation of the specified single function when the supplied single operation completes.

    \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.}
    \return An op handle
    \param req A precondition op handle
    \param callback A pair of op flag and bound completion handler function of type `completion_t`
    \ingroup dispatcher__completion
    \qbk{distinguish, single bound function}
    \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
    \exceptionmodelstd
    \qexample{completion_example1}
    */
    inline future<> completion(const future<> &req, const std::pair<async_op_flags, std::function<dispatcher::completion_t>> &callback);

    /* \brief Schedule a batch of asynchronous invocations of the specified bound functions when their supplied preconditions complete.

    \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.}
    This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t`
    handler specification and calls the specified arbitrary callable, always returning completion on exit.
    
    \return A pair with a batch of futures returning the result of each of the callables and a batch of op handles.
    \tparam "class R" A compiler deduced return type of the bound functions.
    \param ops A batch of precondition op handles. If default constructed, a precondition is null.
    \param callables A batch of bound functions to call, returning R.
    \ingroup dispatcher__call
    \qbk{distinguish, batch bound functions}
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.}
    \exceptionmodelstd
    \qexample{call_example}
    */
    template<class R> inline std::vector<future<R>> call(const std::vector<future<>> &ops, const std::vector<std::function<R()>> &callables);
    /* \brief Schedule a batch of asynchronous invocations of the specified bound functions when their supplied preconditions complete.

    \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.}
    This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t`
    handler specification and calls the specified arbitrary callable, always returning completion on exit. If you
    are seeing performance issues, using `completion()` directly will have much less overhead.
    
    \return A pair with a batch of futures returning the result of each of the callables and a batch of op handles.
    \tparam "class R" A compiler deduced return type of the bound functions.
    \param callables A batch of bound functions to call, returning R.
    \ingroup dispatcher__call
    \qbk{distinguish, batch bound functions without preconditions}
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.}
    \exceptionmodelstd
    \qexample{call_example}
    */
    template<class R> std::vector<future<R>> call(const std::vector<std::function<R()>> &callables) { return call(std::vector<future<>>(), callables); }
    /* \brief Schedule an asynchronous invocation of the specified bound function when its supplied precondition completes.

    \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.}
    This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t`
    handler specification and calls the specified arbitrary callable, always returning completion on exit. If you
    are seeing performance issues, using `completion()` directly will have much less overhead.
    
    \return A pair with a stl_future returning the result of the callable and an op handle.
    \tparam "class R" A compiler deduced return type of the bound functions.
    \param req A precondition op handle. If default constructed, the precondition is null.
    \param callback A bound functions to call, returning R.
    \ingroup async_file_io_dispatcher__call
    \qbk{distinguish, single bound function}
    \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
    \exceptionmodelstd
    \qexample{call_example}
    */
    template<class R> inline future<R> call(const future<> &req, std::function<R()> callback);

    
    
         
    /* \brief Schedule an asynchronous invocation of the specified unbound callable when its supplied precondition completes.
    Note that this function essentially calls `std::bind()` on the callable and the args and passes it to the other call() overload taking a `std::function<>`.
    You should therefore use `std::ref()` etc. as appropriate.

    This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t`
    handler specification and calls the specified arbitrary callable, always returning completion on exit. If you
    are seeing performance issues, using `completion()` directly will have much less overhead.
    
    \return A pair with a stl_future returning the result of the callable and an op handle.
    \tparam "class C" Any callable type.
    \tparam Args Any sequence of argument types.
    \param req A precondition op handle. If default constructed, the precondition is null.
    \param callback An unbound callable to call.
    \param args An arbitrary sequence of arguments to bind to the callable.
    \ingroup dispatcher__call
    \qbk{distinguish, single unbound callable}
    \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
    \exceptionmodelstd
    \qexample{call_example}
    */
#ifndef DOXYGEN_SHOULD_SKIP_THIS
    template<class C, class... Args> inline future<typename detail::vs2013_variadic_overload_resolution_workaround<C, Args...>::type> call(const future<> &req, C callback, Args... args);
#else
    template<class C, class... Args> inline future<typename std::result_of<C(Args...)>::type> call(const future<> &req, C callback, Args... args);
#endif



    /* \brief Schedule a batch of third party handle adoptions.

    \docs_adopt

    \return A batch of op handles.
    \param hs A batch of handles to adopt.
    \ingroup dispatcher__filedirops
    \qbk{distinguish, batch}
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.}
    \exceptionmodelstd
    \qexample{adopt_example}
    */
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> adopt(const std::vector<handle_ptr> &hs);
#endif
    /*! \brief Schedule a batch of asynchronous directory creations and opens after optional preconditions.

    \docs_dir
    \ntkernelnamespacenote

    \return A batch of op handles.
    \param reqs A batch of `path_req` structures.
    \ingroup dir
    \qbk{distinguish, batch}
    \raceguarantees{
    [raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
    [raceguarantee OS X..No guarantees.]
    }
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if directory creation is constant time.}
    \exceptionmodelstd
    \qexample{filedir_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> dir(const std::vector<path_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Schedule a batch of asynchronous directory deletions after optional preconditions.

    \docs_rmdir
    \ntkernelnamespacenote
    
    \return A batch of op handles.
    \param reqs A batch of `path_req` structures.
    \ingroup rmdir
    \qbk{distinguish, batch}
    \raceguarantees{
    [raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
    [raceguarantee Windows..Race free if handle open, else up to the containing directory.]
    [raceguarantee OS X..No guarantees.]
    }
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if directory deletion is constant time.}
    \exceptionmodelstd
    \qexample{filedir_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmdir(const std::vector<path_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Schedule a batch of asynchronous file creations and opens after optional preconditions.
    
    \docs_file
    \ntkernelnamespacenote
    
    \return A batch of op handles.
    \param reqs A batch of `path_req` structures.
    \ingroup file
    \qbk{distinguish, batch}
    \raceguarantees{
    [raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
    [raceguarantee OS X..No guarantees.]
    }
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if file creation is constant time.}
    \exceptionmodelstd
    \qexample{filedir_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> file(const std::vector<path_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Schedule a batch of asynchronous file deletions after optional preconditions.
    
    \docs_rmfile
    \ntkernelnamespacenote

    \return A batch of op handles.
    \param reqs A batch of `path_req` structures.
    \ingroup rmfile
    \qbk{distinguish, batch}
    \raceguarantees{
    [raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
    [raceguarantee Windows..Race free if handle open, else up to the containing directory.]
    [raceguarantee OS X..No guarantees.]
    }
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if file deletion is constant time.}
    \exceptionmodelstd
    \qexample{filedir_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmfile(const std::vector<path_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Schedule a batch of asynchronous symlink creations and opens after a precondition.

    \docs_symlink
    \ntkernelnamespacenote

    \return A batch of op handles.
    \param reqs A batch of `path_req` structures.
    \param targets An optional batch of targets if creating symlinks.
    \ingroup symlink
    \qbk{distinguish, batch}
    \raceguarantees{
    [raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
    [raceguarantee OS X..No guarantees.]
    }
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if symlink creation is constant time.}
    \exceptionmodelstd
    \qexample{filedir_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> symlink(const std::vector<path_req> &reqs, const std::vector<future<>> &targets=std::vector<future<>>()) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Schedule a batch of asynchronous symlink deletions after optional preconditions.
    
    \docs_rmsymlink
    \return A batch of op handles.
    \param reqs A batch of `path_req` structures.
    \ingroup rmsymlink
    \qbk{distinguish, batch}
    \raceguarantees{
    [raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
    [raceguarantee Windows..Race free if handle open, else up to the containing directory.]
    [raceguarantee OS X..No guarantees.]
    }
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if symlink deletion is constant time.}
    \exceptionmodelstd
    \qexample{filedir_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmsymlink(const std::vector<path_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Schedule a batch of asynchronous content synchronisations with physical storage after preceding operations.
   
    \docs_sync

    \return A batch of op handles.
    \param ops A batch of op handles.
    \ingroup sync
    \qbk{distinguish, batch}
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if content synchronisation is constant time (which is extremely unlikely).}
    \exceptionmodelstd
    \qexample{readwrite_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> sync(const std::vector<future<>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Schedule a batch of asynchronous zeroing and deallocations of physical storage ("hole punching") after preceding operations.

    \docs_zero
    
    \return A batch of op handles.
    \param ops A batch of op handles.
    \param ranges A batch of vectors of extents to zero and deallocate.
    \ingroup zero
    \qbk{distinguish, batch}
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if deallocation is constant time.}
    \exceptionmodelstd
    \qexample{extents_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> zero(const std::vector<future<>> &ops, const std::vector<std::vector<std::pair<off_t, off_t>>> &ranges) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Schedule a batch of asynchronous file or directory handle closes after preceding operations.

    \docs_close

    \return A batch of op handles.
    \param ops A batch of op handles.
    \ingroup close
    \qbk{distinguish, batch}
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if closing handles is constant time.}
    \exceptionmodelstd
    \qexample{filedir_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> close(const std::vector<future<>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC

    /*! \brief Schedule a batch of asynchronous data reads after preceding operations, where
    offset and total data read must not exceed the present file size.

    \docs_read
    \direct_io_note

    \return A batch of op handles.
    \tparam "class T" Any type.
    \param ops A batch of io_req<T> structures.
    \ingroup read
    \qbk{distinguish, batch}
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if reading data is constant time.}
    \exceptionmodelstd
    \qexample{readwrite_example}
    */
#ifndef DOXYGEN_SHOULD_SKIP_THIS
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> read(const std::vector<detail::io_req_impl<false>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    template<class T> inline std::vector<future<>> read(const std::vector<io_req<T>> &ops);
#else
    template<class T> BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> read(const std::vector<io_req<T>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
#endif
    /*! \brief Schedule a batch of asynchronous data writes after preceding operations, where
    offset and total data written must not exceed the present file size.

    \docs_write
    \direct_io_note

    \return A batch of op handles.
    \tparam "class T" Any type.
    \param ops A batch of io_req<const T> structures.
    \ingroup write
    \qbk{distinguish, batch}
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if writing data is constant time.}
    \exceptionmodelstd
    \qexample{readwrite_example}
    */
#ifndef DOXYGEN_SHOULD_SKIP_THIS
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> write(const std::vector<detail::io_req_impl<true>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    template<class T> inline std::vector<future<>> write(const std::vector<io_req<T>> &ops);
#else
    template<class T> BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> write(const std::vector<io_req<const T>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
#endif

    /*! \brief Schedule a batch of asynchronous file length truncations after preceding operations.
    
    \docs_truncate

    \return A batch of op handles.
    \param ops A batch of op handles.
    \param sizes A batch of new lengths.
    \ingroup truncate
    \qbk{distinguish, batch}
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if truncating file lengths is constant time.}
    \exceptionmodelstd
    \qexample{readwrite_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> truncate(const std::vector<future<>> &ops, const std::vector<off_t> &sizes) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    
    /*! \brief Schedule a batch of asynchronous directory enumerations after preceding operations.

    \docs_enumerate

    \return A batch of stl_future vectors of directory entries with boolean returning false if done.
    \param reqs A batch of enumeration requests.
    \ingroup enumerate
    \qbk{distinguish, batch}
    \raceguarantees{
    [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
    many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
    comparing inodes for equivalence to a direntry() won't help you.]
    [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
    birthtim, sparse, compressed.]
    }
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of entries in each directory.}
    \exceptionmodelstd
    \qexample{enumerate_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<std::pair<std::vector<directory_entry>, bool>>> enumerate(const std::vector<enumerate_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Schedule a batch of asynchronous extent enumerations after preceding operations.

    \docs_extents

    \return A batch of stl_future vectors of extents.
    \param ops A batch of op handles.
    \ingroup extents
    \qbk{distinguish, batch}
    \raceguarantees{
    [raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
    before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
    into single extents.]
    [raceguarantee Windows..Race free.]
    }
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of extents in each file.}
    \exceptionmodelstd
    \qexample{extents_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<std::vector<std::pair<off_t, off_t>>>> extents(const std::vector<future<>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
    /*! \brief Schedule a batch of asynchronous volume enumerations after preceding operations.

    \docs_statfs

    \return A batch of stl_future volume metadatas.
    \param ops A batch of op handles.
    \param reqs A batch of metadata requests.
    \ingroup statfs
    \qbk{distinguish, batch}
    \raceguarantees{
    [raceguarantee FreeBSD, OS X..Race free.]
    [raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
    flags.rdonly, flags.noexec, flags.nosuid.]
    [raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
    is fetched separately.]
    }
    \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of entries in each directory.}
    \exceptionmodelstd
    \qexample{statfs_example}
    */
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<statfs_t>> statfs(const std::vector<future<>> &ops, const std::vector<fs_metadata_flags> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC

#ifndef DOXYGEN_SHOULD_SKIP_THIS
    inline future<> adopt(handle_ptr h);
    inline future<> dir(const path_req &req);
    inline future<> rmdir(const path_req &req);
    inline future<> file(const path_req &req);
    inline future<> rmfile(const path_req &req);
    inline future<> symlink(const path_req &req, const future<> &target=future<>());
    inline future<> rmsymlink(const path_req &req);
    inline future<> sync(const future<> &req);
    inline future<> zero(const future<> &req, const std::vector<std::pair<off_t, off_t>> &ranges);
    inline future<> close(const future<> &req);
    inline future<> read(const detail::io_req_impl<false> &req);
    inline future<> write(const detail::io_req_impl<true> &req);
    inline future<> truncate(const future<> &op, off_t newsize);
    inline future<std::pair<std::vector<directory_entry>, bool>> enumerate(const enumerate_req &req);
    inline future<std::vector<std::pair<off_t, off_t>>> extents(const future<> &op);
    inline future<statfs_t> statfs(const future<> &op, const fs_metadata_flags &req);

    // Undocumented deliberately
    BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> lock(const std::vector<lock_req> &req) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC

    
    /*! \brief Schedule an asynchronous synchronisation of preceding operations.
    \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls when_all_p() on the futures.}
    If you perform many asynchronous operations of unequal duration but wish to schedule one of more operations
    to occur only after \b all of those operations have completed, this is the correct function to use. The returned
    batch of ops exactly match the input batch of ops (including their exception states), but they will only
    complete when the last of the input batch of ops completes.
    
    \note If an input op is in an exceptioned state at the point of entry into this function, this function
    will propagate the exception there and then. \em Only error states which occur \em after this function
    has been scheduled are propagated into the output set of ops.
    
    \return A batch of op handles.
    \param ops A batch of op handles.
    \ingroup dispatcher__barrier
    \qbk{distinguish, batch}
    \complexity{Amortised O(N) to dispatch. Amortised O(N) to complete.}
    \exceptionmodel{See detailed description above.}
    \qexample{barrier_example}
    */
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> barrier(const std::vector<future<>> &ops);
#endif

    /*! \brief Schedule the return of an op handle after another op handle completes. This is useful when you
    need to supply one op handle to a function but it must not begin until another op handle has finished.
        
    \return The op handle op.
    \param precondition The op handle which must complete for op to be passed through.
    \param op The op handle to return.
    \ingroup dispatcher__depends
    \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
    \exceptionmodelstd
    \qexample{filecopy_example}
    */
    inline future<> depends(future<> precondition, future<> op);

    /*! \brief Completes an operation with a handle or an error, usually used when an operation was previously deferred.

    \ingroup dispatcher__misc
    \qbk{distinguish, normal}
    \complexity{O(N) where N is the number of completions dependent on this op.}
    \exceptionmodel{Should not throw any exception except for out of memory.}
    */
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void complete_async_op(size_t id, handle_ptr h, exception_ptr e=exception_ptr());
    /*! \brief Completes an operation with an error, usually used when an operation was previously deferred.

    \ingroup dispatcher__misc
    \qbk{distinguish, errored}
    \complexity{O(N) where N is the number of completions dependent on this op.}
    \exceptionmodel{Should not throw any exception except for out of memory.}
    */
    void complete_async_op(size_t id, exception_ptr e) { complete_async_op(id, handle_ptr(), e); }
protected:
    template<class F> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC handle_ptr int_get_handle_to_containing_dir(F *parent, size_t id, path_req req, completion_returntype(F::*dofile)(size_t, future<>, path_req));
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC completion_returntype invoke_user_completion_fast(size_t id, future<> h, completion_t *callback);
    BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC completion_returntype invoke_user_completion_slow(size_t id, future<> h, std::function<completion_t> callback);

    template<class F> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> chain_async_ops(int optype, const std::vector<future<>> &preconditions, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, future<>));
    template<class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> chain_async_ops(int optype, const std::vector<future<>> &preconditions, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T));
    template<class R, class F> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<R>> chain_async_ops(int optype, const std::vector<future<>> &preconditions, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, std::shared_ptr<promise<R>>));
    template<class R, class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<R>> chain_async_ops(int optype, const std::vector<future<>> &preconditions, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T, std::shared_ptr<promise<R>>));
    template<class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> chain_async_ops(int optype, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T));
    template<class R, class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<R>> chain_async_ops(int optype, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T, std::shared_ptr<promise<R>>));

    template<class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher::completion_returntype dobarrier(size_t id, future<> h, T);
    template<class F, class... Args> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC handle_ptr invoke_async_op_completions(size_t id, future<> h, completion_returntype(F::*f)(size_t, future<>, Args...), Args... args);
    template<class F, class... Args> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> chain_async_op(detail::immediate_async_ops &immediates, int optype, const future<> &precondition, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, Args...), Args... args);
};
/*! \brief Instatiates the best available async_file_io_dispatcher implementation for this system for the given uri.

Note that the number of threads in the threadpool supplied is the maximum non-async op queue depth (e.g. file opens, closes etc.).
For fast SSDs, there isn't much gain after eight-sixteen threads, so the process threadpool is set to eight by default.
For slow hard drives, or worse, SANs, a queue depth of 64 or higher might deliver significant benefits.

URIs currently supported by AFIO:
- <b>`__fileurl__`</b> The dispatcher will refer to the local filesystem of this machine.

\return A shared_ptr to the best available async_file_io_dispatcher implementation for this system for the given uri.
\param uri Where to open the dispatcher upon.
\param flagsforce The flags to bitwise OR with any opened file flags. Used to force on certain flags.
\param flagsmask The flags to bitwise AND with any opened file flags. Used to force off certain flags.
\param threadpool The threadpool instance to use for asynchronous dispatch.
\ingroup async_file_io_dispatcher
\qbk{
[heading Example]
[call_example]
}
*/
#ifdef DOXYGEN_SHOULD_SKIP_THIS
BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<dispatcher_ptr> make_dispatcher(std::string uri="file : / / /", file_flags flagsforce = file_flags::none, file_flags flagsmask = file_flags::none, std::shared_ptr<thread_source> threadpool = process_threadpool()) noexcept;
#else
BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<dispatcher_ptr> make_dispatcher(std::string uri="file:///", file_flags flagsforce=file_flags::none, file_flags flagsmask=file_flags::none, std::shared_ptr<thread_source> threadpool = process_threadpool()) noexcept;
#endif

namespace detail
{
    struct when_all_state : std::enable_shared_from_this<when_all_state>
    {
        promise<std::vector<handle_ptr>> out;
        std::vector<shared_future<handle_ptr>> in;
    };
    template<bool rethrow> inline void when_all_ops_do(std::shared_ptr<when_all_state> state)
    {
        // If we're on Boost.Thread, coalesce all wait ops into a single
#if BOOST_AFIO_USE_BOOST_THREAD
        boost::wait_for_all(state->in.begin(), state->in.end());
#endif
        std::vector<handle_ptr> ret;
        ret.reserve(state->in.size());
        for(auto &i: state->in)
        {
            auto e(get_exception_ptr(i));
            if(e)
            {
                if(rethrow)
                {
                    state->out.set_exception(e);
                    return;
                }
                ret.push_back(handle_ptr());
            }
            else
                ret.push_back(i.get());
        }
        state->out.set_value(ret);
    }
    template<bool rethrow, class Iterator> inline stl_future<std::vector<handle_ptr>> when_all_ops(Iterator first, Iterator last)
    {
        auto state=std::make_shared<when_all_state>();
        state->in.reserve(std::distance(first, last));
        for(; first!=last; ++first)
            state->in.push_back(first->_h);
        auto ret=state->out.get_future();
        process_threadpool()->enqueue([BOOST_AFIO_LAMBDA_MOVE_CAPTURE(state)] { when_all_ops_do<rethrow>(std::move(state)); });
        return ret;
    }
    struct when_any_state : std::enable_shared_from_this<when_any_state>
    {
        atomic<size_t> count;
        promise<handle_ptr> out;
        std::vector<shared_future<handle_ptr>> in;
        when_any_state() : count(0) { }
    };
#if BOOST_AFIO_USE_BOOST_THREAD
    // Boost.Thread has wait_for_any() which lets us be more efficient here and wait directly on the futures
    template<bool rethrow> inline void when_any_ops_do(std::shared_ptr<when_any_state> state)
    {
        auto &i=*boost::wait_for_any(state->in.begin(), state->in.end());
        auto e(get_exception_ptr(i));
        if(e)
        {
            if(rethrow)
            {
                state->out.set_exception(e);
                return;
            }
            state->out.set_value(handle_ptr());
        }
        else
            state->out.set_value(i.get());
    }
    template<bool rethrow, class Iterator> inline stl_future<handle_ptr> when_any_ops(Iterator first, Iterator last)
    {
        auto state=std::make_shared<when_any_state>();
        state->in.reserve(std::distance(first, last));
        for(; first!=last; ++first)
            state->in.push_back(first->h);
        auto ret=state->out.get_future();
        process_threadpool()->enqueue([BOOST_AFIO_LAMBDA_MOVE_CAPTURE(state)]{ when_any_ops_do<rethrow>(std::move(state)); });
        return ret;
    }
#else
    // Without wait_for_any, schedule a completion onto every op and the first to fire wins
    template<bool rethrow> inline std::pair<bool, handle_ptr> when_any_ops_do(std::shared_ptr<when_any_state> state, size_t idx, size_t id, future<> h)
    {
        auto &i=state->in[idx];
        if(0==state->count.fetch_add(1, memory_order_relaxed))  // Will be zero exactly once
        {
            auto e(get_exception_ptr(i));
            if(e)
            {
                if(rethrow)
                {
                    state->out.set_exception(e);
                    return std::make_pair(true, handle_ptr());
                }
                state->out.set_value(handle_ptr());
            }
            else
                state->out.set_value(i.get());
        }
        return std::make_pair(true, handle_ptr());
    }
    template<bool rethrow, class Iterator> inline stl_future<handle_ptr> when_any_ops(Iterator first, Iterator last)
    {
        auto state=std::make_shared<when_any_state>();
        auto dispatcher=first->parent();
        std::vector<future<>> ops(first, last);
        state->in.reserve(ops.size());
        for(auto &op : ops)
            state->in.push_back(op._h);
        auto ret=state->out.get_future();
        typedef std::function<typename dispatcher::completion_t> ft;
        std::vector<std::pair<async_op_flags, ft>> completions;
        completions.reserve(ops.size());
        for(size_t n=0; n<ops.size(); n++)
          completions.push_back(std::make_pair(async_op_flags::immediate, std::bind(&when_any_ops_do<rethrow>, state, n, std::placeholders::_1, std::placeholders::_2)));
        dispatcher->completion(ops, completions);
        return ret;
    }
#endif
    template<bool is_all> struct select_when_ops_return_type
    {
        typedef stl_future<std::vector<handle_ptr>> type; // when_all_p()
    };
    template<> struct select_when_ops_return_type<false>
    {
        typedef stl_future<handle_ptr> type; // when_any()
    };
    template<bool is_all, class T> struct enable_if_async_op
    {
        //static_assert(std::is_same<T, T>::value, "Not an iterator of future<>");
    };
    template<bool is_all, class T> struct enable_if_async_op<is_all, future<T>>
    {
        typedef typename select_when_ops_return_type<is_all>::type type;
    };
}

/*! \brief Returns a result when all the supplied ops complete. Does not propagate exception states.

\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
\return A stl_future vector of shared_ptr's to handle.
\tparam "class Iterator" An iterator type.
\param _ An instance of std::nothrow_t.
\param first An iterator pointing to the first future<> to wait upon.
\param last An iterator pointing after the last future<> to wait upon.
\ingroup when_all_ops
\qbk{distinguish, iterator batch of ops not exception propagating}
\complexity{O(N).}
\exceptionmodel{Non propagating}
*/
template<class Iterator> inline typename detail::enable_if_async_op<true, typename Iterator::value_type>::type when_all_p(std::nothrow_t _, Iterator first, Iterator last)
{
    if(first==last)
        return stl_future<std::vector<handle_ptr>>();
    return detail::when_all_ops<false>(first, last);
}
/*! \brief Returns a result when any the supplied ops complete. Does not propagate exception states.

\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
\return A stl_future vector of shared_ptr's to handle.
\tparam "class Iterator" An iterator type.
\param _ An instance of std::nothrow_t.
\param first An iterator pointing to the first future<> to wait upon.
\param last An iterator pointing after the last future<> to wait upon.
\ingroup when_all_ops
\qbk{distinguish, iterator batch of ops not exception propagating}
\complexity{O(N).}
\exceptionmodel{Non propagating}
*/
template<class Iterator> inline typename detail::enable_if_async_op<false, typename Iterator::value_type>::type when_any(std::nothrow_t _, Iterator first, Iterator last)
{
    if(first==last)
        return stl_future<handle_ptr>();
    return detail::when_any_ops<false>(first, last);
}
/*! \brief Returns a result when all the supplied ops complete. Does not propagate exception states.

\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
\return A stl_future vector of shared_ptr's to handle.
\param _ An instance of std::nothrow_t.
\param ops A vector of the async_io_ops to wait upon.
\ingroup when_all_ops
\qbk{distinguish, vector batch of ops not exception propagating}
\complexity{O(N).}
\exceptionmodel{Non propagating}
*/
template<class T> inline stl_future<std::vector<handle_ptr>> when_all_p(std::nothrow_t _, std::vector<future<T>> ops)
{
    if(ops.empty())
        return stl_future<std::vector<handle_ptr>>();
    return detail::when_all_ops<false>(ops.begin(), ops.end());
}
/*! \brief Returns a result when any the supplied ops complete. Does not propagate exception states.

\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
\return A stl_future vector of shared_ptr's to handle.
\param _ An instance of std::nothrow_t.
\param ops A vector of the async_io_ops to wait upon.
\ingroup when_all_ops
\qbk{distinguish, vector batch of ops not exception propagating}
\complexity{O(N).}
\exceptionmodel{Non propagating}
*/
template<class T> inline stl_future<handle_ptr> when_any(std::nothrow_t _, std::vector<future<T>> ops)
{
    if(ops.empty())
        return stl_future<handle_ptr>();
    return detail::when_any_ops<false>(ops.begin(), ops.end());
}
/*! \brief Returns a result when all the supplied ops complete. Propagates exception states.

\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
\return A stl_future vector of shared_ptr's to handle.
\tparam "class Iterator" An iterator type.
\param first An iterator pointing to the first future<> to wait upon.
\param last An iterator pointing after the last future<> to wait upon.
\ingroup when_all_ops
\qbk{distinguish, iterator batch of ops exception propagating}
\complexity{O(N).}
\exceptionmodel{Propagating}
*/
template<class Iterator> inline typename detail::enable_if_async_op<true, typename Iterator::value_type>::type when_all_p(Iterator first, Iterator last)
{
    if(first==last)
        return stl_future<std::vector<handle_ptr>>();
    return detail::when_all_ops<true>(first, last);
}
/*! \brief Returns a result when any the supplied ops complete. Propagates exception states.

\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
\return A stl_future vector of shared_ptr's to handle.
\tparam "class Iterator" An iterator type.
\param first An iterator pointing to the first future<> to wait upon.
\param last An iterator pointing after the last future<> to wait upon.
\ingroup when_all_ops
\qbk{distinguish, iterator batch of ops exception propagating}
\complexity{O(N).}
\exceptionmodel{Propagating}
*/
template<class Iterator> inline typename detail::enable_if_async_op<false, typename Iterator::value_type>::type when_any(Iterator first, Iterator last)
{
    if(first==last)
        return stl_future<handle_ptr>();
    return detail::when_any_ops<true>(first, last);
}
/*! \brief Returns a result when all the supplied ops complete. Propagates exception states.

\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
\return A stl_future vector of shared_ptr's to handle.
\param ops A vector of the async_io_ops to wait upon.
\ingroup when_all_ops
\qbk{distinguish, vector batch of ops exception propagating}
\complexity{O(N).}
\exceptionmodel{Propagating}
*/
template<class T> inline stl_future<std::vector<handle_ptr>> when_all_p(std::vector<future<T>> ops)
{
    if(ops.empty())
        return stl_future<std::vector<handle_ptr>>();
    return detail::when_all_ops<true>(ops.begin(), ops.end());
}
/*! \brief Returns a result when any the supplied ops complete. Propagates exception states.

\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
\return A stl_future vector of shared_ptr's to handle.
\param ops A vector of the async_io_ops to wait upon.
\ingroup when_all_ops
\qbk{distinguish, vector batch of ops exception propagating}
\complexity{O(N).}
\exceptionmodel{Propagating}
*/
template<class T> inline stl_future<handle_ptr> when_any(std::vector<future<T>> ops)
{
    if(ops.empty())
        return stl_future<handle_ptr>();
    return detail::when_any_ops<true>(ops.begin(), ops.end());
}
/*! \brief Returns a result when the supplied op completes. Does not propagate exception states.

\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
\return A stl_future vector of shared_ptr's to handle.
\param _ An instance of std::nothrow_t.
\param op An future<> to wait upon.
\ingroup when_all_ops
\qbk{distinguish, convenience single op not exception propagating}
\complexity{O(1).}
\exceptionmodel{Non propagating}
*/
template<class T> inline stl_future<std::vector<handle_ptr>> when_all_p(std::nothrow_t _, future<T> op)
{
    std::vector<future<T>> ops(1, op);
    return when_all_p(_, ops);
}
/*! \brief Returns a result when the supplied op completes. Propagates exception states.

\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
\return A stl_future vector of shared_ptr's to handle.
\param ops A sequence of future<> to wait upon.
\ingroup when_all_ops
\qbk{distinguish, convenience multiple op exception propagating}
\complexity{O(N).}
\exceptionmodel{Propagating}
*/
template<class... Types> inline stl_future<std::vector<handle_ptr>> when_all_p(future<Types> &... ops)
{
    std::vector<future<>> _ops = { std::forward<future<Types> &>(ops)... };
    return when_all_p(_ops);
}

/*! \struct path_req
\brief A convenience bundle of path and flags, with optional precondition. Paths may be a path fragment (relative to the precondition) or absolute, in which case
if necessary they are made canonical and absolute in the constructor according to the current working directory.

\qbk{
[include generated/struct_path_req_1_1absolute.qbk]
[include generated/struct_path_req_1_1relative.qbk]
}
*/
struct path_req
{
    bool is_relative;           //!< Whether the precondition is also where this path begins
    BOOST_AFIO_V2_NAMESPACE::path path;            //!< The filing system path to be used for this operation
    file_flags flags;           //!< The flags to be used for this operation (note they can be overriden by flags passed during dispatcher construction).
    future<> precondition;   //!< An optional precondition for this operation
    //! \brief Tags the path as being absolute
    struct absolute;
    //! \brief Tags the path as being relative
    struct relative;
    //! \constr
    path_req() : is_relative(false), flags(file_flags::none) { }
    //! \cconstr
    path_req(const path_req &o) = default;
    //! \mconstr
    path_req(path_req &&o) noexcept : is_relative(o.is_relative), path(std::move(o.path)), flags(std::move(o.flags)), precondition(std::move(o.precondition)) { }
    //! \mconstr
    inline path_req(absolute &&o);
    //! \mconstr
    inline path_req(relative &&o);
    /*! \brief Constructs an instance.
    
    \tparam "class T" The type of path to be used.
    \param _path The filing system path to be used.
    \param _flags The flags to be used.
    */

    template<class T, typename=typename std::enable_if<!std::is_constructible<path_req, T>::value && !std::is_constructible<future<>, T>::value>::type> path_req(T &&_path, file_flags _flags=file_flags::none) : is_relative(false), path(BOOST_AFIO_V2_NAMESPACE::path::make_absolute(std::forward<T>(_path))), flags(_flags) { }
    /*! \brief Constructs an instance.
    
    \tparam "class T" The type of path to be used.
    \param _is_relative Whether the precondition is where the path begins
    \param _precondition The precondition for this operation.
    \param _path The filing system path to be used.
    \param _flags The flags to be used.
    */
    template<class T, typename=typename std::enable_if<!std::is_convertible<BOOST_AFIO_V2_NAMESPACE::path, T>::value>::type> path_req(bool _is_relative, future<> _precondition, T &&_path, file_flags _flags=file_flags::none) : is_relative(_is_relative), path(_is_relative ? BOOST_AFIO_V2_NAMESPACE::path(std::forward<T>(_path)) : BOOST_AFIO_V2_NAMESPACE::path(BOOST_AFIO_V2_NAMESPACE::path::make_absolute(std::forward<T>(_path)))), flags(_flags), precondition(std::move(_precondition)) { _validate(); }
    //! \overload
    path_req(bool _is_relative, future<> _precondition, BOOST_AFIO_V2_NAMESPACE::path _path, file_flags _flags=file_flags::none) : is_relative(_is_relative), path(std::move(_path)), flags(_flags), precondition(std::move(_precondition)) { _validate(); }
    /*! \brief Constructs an instance.
    
    \param _precondition The precondition for this operation (used as the path).
    \param _flags The flags to be used.
    */
    path_req(future<> _precondition, file_flags _flags=file_flags::none) : is_relative(true), flags(_flags), precondition(std::move(_precondition)) { _validate(); }
    //! Validates contents
    bool validate() const
    {
        if(!is_relative && path.empty()) return false;
        return !precondition.valid() || precondition.validate();
    }
protected:
    void _validate() const
    {
#if BOOST_AFIO_VALIDATE_INPUTS
        if(!validate())
            BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
    }
};
//! Convenience tag type constructing a relative path path_req
struct path_req::relative : path_req
{
  /*! \brief Constructs an instance.
  
  \tparam "class T" The type of path to be used.
  \param _precondition The precondition for this operation.
  \param _path The filing system path to be used.
  \param _flags The flags to be used.
  */
  template<class T> relative(future<> _precondition, T &&_path, file_flags _flags=file_flags::none) : path_req(true, std::move(_precondition), std::forward<T>(_path), _flags) { _validate(); }
  /*! \brief Constructs an instance.
  
  \param _precondition The precondition for this operation.
  \param _flags The flags to be used.
  */
  relative(future<> _precondition, file_flags _flags=file_flags::none) : path_req(std::move(_precondition), _flags) { _validate(); }
};
//! Convenience tag type constructing an absolute path path_req
struct path_req::absolute : path_req
{
  /*! \brief Constructs an instance.
  
  \tparam "class T" The type of path to be used.
  \param _precondition The precondition for this operation.
  \param _path The filing system path to be used.
  \param _flags The flags to be used.
  */
  template<class T> absolute(future<> _precondition, T &&_path, file_flags _flags=file_flags::none) : path_req(false, std::move(_precondition), std::move(BOOST_AFIO_V2_NAMESPACE::path::make_absolute(std::forward<T>(_path))), _flags) { _validate(); }
};
inline path_req::path_req(path_req::absolute &&o) : is_relative(o.is_relative), path(std::move(o.path)), flags(std::move(o.flags)), precondition(std::move(o.precondition)) { }
inline path_req::path_req(path_req::relative &&o) : is_relative(o.is_relative), path(std::move(o.path)), flags(std::move(o.flags)), precondition(std::move(o.precondition)) { }

/*! \defgroup to_asio_buffers Overloadable free functions converting the types passed to io_req<> into an asio buffer sequence for read() and write().

You can add your own free function overloads to tell AFIO how to convert your custom types into ASIO scatter gather buffers.
Note that const types must convert into asio::const_buffer, and non-const types must convert into asio::mutable_buffer. It
is entirely acceptable for types to allow writing (const) only and not reading.

Default overloads provided are as follows:
 - Any trivial type T * and number of items.
 - void * and const void * with number of bytes.
 - C array types are treated as if a std::array.
 - asio::const_buffer and asio::mutable_buffer are passed through as-is.
 - Any container type holding a trivial type T. This includes std::basic_string (write only), std::vector and std::array,
   all three of which are specially collapsed into a single scatter gather as they guarantee storing their
   contents contiguously.
 - Any container type holding any of the above, including other containers. These will be converted into
   scatter gather lists for you. Note that the constness of the type returned by the container's iterator is respected,
   so if the container iterator returns a const reference (e.g. std::basic_string) then you cannot gather read into
   that container, and instead should receive a compile time error.
*/
template<class T> inline std::vector<asio::mutable_buffer> to_asio_buffers(T &v);
template<class T> inline std::vector<asio::const_buffer> to_asio_buffers(const T &v);
template<class T, size_t N> inline std::vector<asio::mutable_buffer> to_asio_buffers(T (&v)[N]);
template<class T, size_t N> inline std::vector<asio::const_buffer> to_asio_buffers(const T (&v)[N]);
/*! \brief Passing through asio::mutable_buffer

\return A vector of ASIO buffers
\ingroup to_asio_buffers
\qbk{distinguish, asio mutable_buffer}
*/
inline std::vector<asio::mutable_buffer> to_asio_buffers(asio::mutable_buffer &v)
{
  return std::vector<asio::mutable_buffer>(1, v);
}
/*! \brief Passing through asio::const_buffer

\return A vector of ASIO buffers
\ingroup to_asio_buffers
\qbk{distinguish, asio const_buffer}
*/
inline std::vector<asio::const_buffer> to_asio_buffers(asio::const_buffer &v)
{
  return std::vector<asio::const_buffer>(1, v);
}
/*! \brief A buffer at v sized length*sizeof(T)

\tparam "class T" Any trivial type T
\return A vector of ASIO buffers
\ingroup to_asio_buffers
\qbk{distinguish, buffer of T}
*/
template<class T> inline std::vector<asio::mutable_buffer> to_asio_buffers(T *v, size_t length)
{
  static_assert(std::is_trivial<T>::value, "to_asio_buffers<T> has not been specialised for this non-trivial type, which suggests you are trying to read or write a complex C++ type! Either add a custom specialisation, or directly instantiate an io_req with a void * and size_t length to some serialised representation.");
  return std::vector<asio::mutable_buffer>(1, asio::mutable_buffer((void *) v, length*sizeof(T)));
}
/*! \brief A buffer at v sized length*sizeof(T)

\tparam "class T" Any trivial type T
\return A vector of ASIO buffers
\ingroup to_asio_buffers
\qbk{distinguish, const buffer of T}
*/
template<class T> inline std::vector<asio::const_buffer> to_asio_buffers(const T *v, size_t length)
{
  static_assert(std::is_trivial<T>::value, "to_asio_buffers<T> has not been specialised for this non-trivial type, which suggests you are trying to read or write a complex C++ type! Either add a custom specialisation, or directly instantiate an io_req with a void * and size_t length to some serialised representation.");
  return std::vector<asio::const_buffer>(1, asio::const_buffer((void *) v, length*sizeof(T)));
}
/*! \brief A buffer at v sized length

\return A vector of ASIO buffers
\ingroup to_asio_buffers
\qbk{distinguish, buffer}
*/
inline std::vector<asio::mutable_buffer> to_asio_buffers(void *v, size_t length)
{
  return std::vector<asio::mutable_buffer>(1, asio::mutable_buffer(v, length));
}
/*! \brief A buffer at v sized length

\return A vector of ASIO buffers
\ingroup to_asio_buffers
\qbk{distinguish, const buffer of T}
*/
inline std::vector<asio::const_buffer> to_asio_buffers(const void *v, size_t length)
{
  return std::vector<asio::const_buffer>(1, asio::const_buffer(v, length));
}
namespace detail
{
    // Length deducing asio buffer conversions
    template<bool is_const, class R, class T, bool is_trivial=std::is_trivial<T>::value, bool is_container=is_container<T>::value> struct to_asio_buffers_helper
    {
      template<class U> std::vector<R> operator()(U &v) const
      {
        static_assert(!std::is_same<T, T>::value, "to_asio_buffers(T) called with type T which is neither trivial nor a container. Did you mean to call io_req with a void * and a byte length, or do you need to overload to_asio_buffers()?");
        static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This type is const, so you cannot generate an asio::mutable_buffer from it.");
        return std::vector<R>();
      }
    };
    // Trivial types get sent as is
    template<bool is_const, class R, class T> struct to_asio_buffers_helper<is_const, R, T, true, false>
    {
      template<class U> std::vector<R> operator()(U &v) const
      {
        static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This type is const, so you cannot generate an asio::mutable_buffer from it.");
        return std::vector<R>(1, R(&v, sizeof(v)));
      }
    };

    // Container types build a scatter gather list of their contents
    template<class R, class C, class T, bool is_const=std::is_const<T>::value, bool is_trivial=std::is_trivial<T>::value> struct container_to_asio_buffers_helper
    {
      template<class U> std::vector<R> operator()(U &v) const
      {
        static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it.");
        std::vector<R> ret;
        for(auto &i : v)
        {
          std::vector<R> item(to_asio_buffers(i));
          ret.reserve(ret.size()+item.size());
          ret.insert(ret.end(), std::make_move_iterator(item.begin()), std::make_move_iterator(item.end()));
        }
        return ret;
      }
    };
    // Container specialisations where we know we can skip scatter gather
    template<class R, class C, class T, class A, class _Ct, bool is_const> struct container_to_asio_buffers_helper<R, std::basic_string<C, T, A>, _Ct, is_const, true>
    {
      template<class U> std::vector<R> operator()(U &v) const
      {
        static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it.");
        return std::vector<R>(1, R(&v.front(), v.size()*sizeof(C)));
      }
    };
    template<class R, class T, class A, class _T, bool is_const> struct container_to_asio_buffers_helper<R, std::vector<T, A>, _T, is_const, true>
    {
      template<class U> std::vector<R> operator()(U &v) const
      {
        static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it.");
        return std::vector<R>(1, R(v.data(), v.size()*sizeof(T)));
      }
    };
    template<class R, class T, size_t N, class _T, bool is_const> struct container_to_asio_buffers_helper<R, std::array<T, N>, _T, is_const, true>
    {
      template<class U> std::vector<R> operator()(U &v) const
      {
        static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it.");
        std::vector<R> ret(1, R(v.data(), v.size()*sizeof(T)));
        return ret;
      }
    };
    template<bool is_const, class R, class T, bool is_trivial> struct to_asio_buffers_helper<is_const, R, T, is_trivial, true> : container_to_asio_buffers_helper<R, T, typename is_container<T>::type>
    {
    };
    // Pass through vectors and arrays of asio buffers
    template<bool is_const, class R> struct to_asio_buffers_helper<is_const, R, std::vector<asio::mutable_buffer>, false, true>
    {
      template<class U> std::vector<R> operator()(U &v) const
      {
        std::vector<R> ret(v.begin(), v.end());
        return ret;
      }
    };
    template<bool is_const> struct to_asio_buffers_helper<is_const, asio::mutable_buffer, std::vector<asio::mutable_buffer>, false, true>
    {
      template<class U> std::vector<asio::mutable_buffer> operator()(U &v) const
      {
        return v;
      }
    };
    template<bool is_const, class R> struct to_asio_buffers_helper<is_const, R, std::vector<asio::const_buffer>, false, true>
    {
      template<class U> std::vector<R> operator()(U &v) const
      {
        std::vector<R> ret(v.begin(), v.end());
        return ret;
      }
    };
    template<bool is_const> struct to_asio_buffers_helper<is_const, asio::const_buffer, std::vector<asio::const_buffer>, false, true>
    {
      template<class U> std::vector<asio::const_buffer> operator()(U &v) const
      {
        return v;
      }
    };
    template<bool is_const, size_t N, class R> struct to_asio_buffers_helper<is_const, R, std::array<asio::mutable_buffer, N>, false, true>
    {
      template<class U> std::vector<R> operator()(U &v) const
      {
        std::vector<R> ret(v.begin(), v.end());
        return ret;
      }
    };
    template<bool is_const, size_t N, class R> struct to_asio_buffers_helper<is_const, R, std::array<asio::const_buffer, N>, false, true>
    {
      template<class U> std::vector<R> operator()(U &v) const
      {
        std::vector<R> ret(v.begin(), v.end());
        return ret;
      }
    };
}
/*! \brief Any trivial type T or STL container.

Trivial types turn into a buffer of &v sized sizeof(T).
Container types have their value type deduced and to_asio_buffers() called on that value_type.
Additional specialisations are provided for string, vector and array to collapse the scatter
gather buffers into a single one for contiguous storage.

\tparam "class T" Any trivial type T or STL container
\return A vector of ASIO buffers
\ingroup to_asio_buffers
\qbk{distinguish, trivial and container types}
*/
template<class T> inline std::vector<asio::mutable_buffer> to_asio_buffers(T &v)
{
  static_assert(!std::is_pointer<T>::value, "You cannot assemble scatter gather buffers from raw pointers, you need to specify a length or supply a type carrying a length");
  return detail::to_asio_buffers_helper<false, asio::mutable_buffer, T>()(v);
}
/*! \brief Any trivial type T or STL container.

Trivial types turn into a buffer of &v sized sizeof(T).
Container types have their value type deduced and to_asio_buffers() called on that value_type.
Additional specialisations are provided for string, vector and array to collapse the scatter
gather buffers into a single one for contiguous storage.

\tparam "class T" Any trivial type T or STL container
\return A vector of ASIO buffers
\ingroup to_asio_buffers
\qbk{distinguish, const trivial and container types}
*/
template<class T> inline std::vector<asio::const_buffer> to_asio_buffers(const T &v)
{
  static_assert(!std::is_pointer<T>::value, "You cannot assemble scatter gather buffers from raw pointers, you need to specify a length or supply a type carrying a length");
  return detail::to_asio_buffers_helper<true, asio::const_buffer, T>()(v);
}
/*! \brief A buffer at v sized N*sizeof(T)

\tparam "class T" Any trivial type T
\return A vector of ASIO buffers
\ingroup to_asio_buffers
\qbk{distinguish, C arrays}
*/
template<class T, size_t N> inline std::vector<asio::mutable_buffer> to_asio_buffers(T (&v)[N])
{
  return to_asio_buffers(reinterpret_cast<std::array<T, N> &>(v));
}
/*! \brief A buffer at v sized N*sizeof(T)

\tparam "class T" Any trivial type T
\return A vector of ASIO buffers
\ingroup to_asio_buffers
\qbk{distinguish, const C arrays}
*/
template<class T, size_t N> inline std::vector<asio::const_buffer> to_asio_buffers(const T (&v)[N])
{
  return to_asio_buffers(reinterpret_cast<const std::array<T, N> &>(v));
}

namespace detail
{
    //! \brief The implementation of all io_req specialisations. \tparam for_writing Whether this implementation is for writing data. \ingroup io_req
    template<bool for_writing> class io_req_impl;
    template<> class io_req_impl<false>
    {
    public:
        //! An optional precondition for this operation
        future<> precondition;
        //! A sequence of mutable Boost.ASIO buffers to read into
        std::vector<asio::mutable_buffer> buffers;
        //! The offset from which to read
        off_t where;
        //! \constr
        io_req_impl() { }
        //! \cconstr
        io_req_impl(const io_req_impl &o) : precondition(o.precondition), buffers(o.buffers), where(o.where) { }
        //! \mconstr
        io_req_impl(io_req_impl &&o) noexcept : precondition(std::move(o.precondition)), buffers(std::move(o.buffers)), where(std::move(o.where)) { }
        //! \cassign
        io_req_impl &operator=(const io_req_impl &o) { precondition=o.precondition; buffers=o.buffers; where=o.where; return *this; }
        //! \massign
        io_req_impl &operator=(io_req_impl &&o) noexcept { precondition=std::move(o.precondition); buffers=std::move(o.buffers); where=std::move(o.where); return *this; }
        //! \io_req2
        io_req_impl(future<> _precondition, std::vector<asio::mutable_buffer> _buffers, off_t _where) : precondition(std::move(_precondition)), buffers(std::move(_buffers)), where(_where) { _validate(); }
        //! Validates contents for correctness \return True if contents are correct
        bool validate() const
        {
            //if(!precondition.validate()) return false;
            if(buffers.empty()) return false;
            for(auto &b: buffers)
            {
                if(!asio::buffer_cast<const void *>(b) || !asio::buffer_size(b)) return false;
                if(precondition.parent() && !!(precondition.parent()->fileflags(file_flags::none)&file_flags::os_direct))
                {
                    if(((size_t) asio::buffer_cast<const void *>(b) & 4095) || (asio::buffer_size(b) & 4095)) return false;
                }
            }
            return true;
        }
    private:
        void _validate() const
        {
#if BOOST_AFIO_VALIDATE_INPUTS
            if(!validate())
                BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
        }
    };
    template<> class io_req_impl<true>
    {
    public:
        //! An optional precondition for this operation
        future<> precondition;
        //! A sequence of mutable Boost.ASIO buffers to read into
        std::vector<asio::const_buffer> buffers;
        //! The offset from which to read
        off_t where;
        //! \constr
        io_req_impl() { }
        //! \cconstr
        io_req_impl(const io_req_impl &o) : precondition(o.precondition), buffers(o.buffers), where(o.where) { }
        //! \mconstr
        io_req_impl(io_req_impl &&o) noexcept : precondition(std::move(o.precondition)), buffers(std::move(o.buffers)), where(std::move(o.where)) { }
        //! \cconstr
        io_req_impl(const io_req_impl<false> &o) : precondition(o.precondition), where(o.where) { buffers.reserve(o.buffers.capacity()); for(auto &i: o.buffers){ buffers.push_back(i); } }
        //! \mconstr
        io_req_impl(io_req_impl<false> &&o) noexcept : precondition(std::move(o.precondition)), where(std::move(o.where)) { buffers.reserve(o.buffers.capacity()); for(auto &&i: o.buffers){ buffers.push_back(std::move(i)); } }
        //! \cassign
        io_req_impl &operator=(const io_req_impl &o) { precondition=o.precondition; buffers=o.buffers; where=o.where; return *this; }
        //! \massign
        io_req_impl &operator=(io_req_impl &&o) noexcept { precondition=std::move(o.precondition); buffers=std::move(o.buffers); where=std::move(o.where); return *this; }
        //! \io_req2
        io_req_impl(future<> _precondition, std::vector<asio::const_buffer> _buffers, off_t _where) : precondition(std::move(_precondition)), buffers(std::move(_buffers)), where(_where) { _validate(); }
        //! \io_req2
        io_req_impl(future<> _precondition, std::vector<asio::mutable_buffer> _buffers, off_t _where) : precondition(std::move(_precondition)), where(_where)
        {
            buffers.reserve(_buffers.capacity());
            for(auto &&i: _buffers)
                buffers.push_back(std::move(i));
            _validate();
        }
        //! Validates contents for correctness \return True if contents are correct
        bool validate() const
        {
            //if(!precondition.validate()) return false;
            if(buffers.empty()) return false;
            for(auto &b: buffers)
            {
                if(!asio::buffer_cast<const void *>(b) || !asio::buffer_size(b)) return false;
                if(precondition.parent() && !!(precondition.parent()->fileflags(file_flags::none)&file_flags::os_direct))
                {
                    if(((size_t) asio::buffer_cast<const void *>(b) & 4095) || (asio::buffer_size(b) & 4095)) return false;
                }
            }
            return true;
        }
    private:
        void _validate() const
        {
#if BOOST_AFIO_VALIDATE_INPUTS
            if(!validate())
                BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
        }
    };
}

/*! \struct io_req
\brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes.

\tparam "class T" Any readable (if const) or writable (if non-const) type T as specified by its to_asio_buffers() overload.
\ingroup io_req
*/
template<class T> struct io_req : public detail::io_req_impl<false>
{
#ifdef DOXYGEN_SHOULD_SKIP_THIS
    //! A precondition containing an open file handle for this operation
    future<> precondition;
    //! A sequence of mutable Boost.ASIO buffers to read into
    std::vector<asio::mutable_buffer> buffers;
    //! The offset from which to read
    off_t where;
#endif
    //! \constr
    io_req() { }
    //! \cconstr
    io_req(const io_req &o) : detail::io_req_impl<false>(o) { }
    //! \mconstr
    io_req(io_req &&o) noexcept : detail::io_req_impl<false>(std::move(o)) { }
    //! \cassign
    io_req &operator=(const io_req &o) { static_cast<detail::io_req_impl<false>>(*this)=o; return *this; }
    //! \massign
    io_req &operator=(io_req &&o) noexcept { static_cast<detail::io_req_impl<false>>(*this)=std::move(o); return *this; }
    //! \io_req1 \param _length The number of items to transfer
    io_req(future<> _precondition, T *v, size_t _length, off_t _where) : detail::io_req_impl<false>(std::move(_precondition), to_asio_buffers(v, _length), _where) { }
    //! \io_req1
    template<class U> io_req(future<> _precondition, U &v, off_t _where) : detail::io_req_impl<false>(std::move(_precondition), to_asio_buffers(v), _where) { }
    //! \io_req1 \tparam N The number of items in the array
    template<class U, size_t N> io_req(future<> _precondition, U (&v)[N], off_t _where) : detail::io_req_impl<false>(std::move(_precondition), to_asio_buffers(v), _where) { }
};
/*!
\brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes.

\tparam "class T" Any readable (if const) or writable (if non-const) type T as specified by its to_asio_buffers() overload.
\ingroup io_req
*/
template<class T> struct io_req<const T> : public detail::io_req_impl<true>
{
#ifdef DOXYGEN_SHOULD_SKIP_THIS
    //! A precondition containing an open file handle for this operation
    future<> precondition;
    //! A sequence of const Boost.ASIO buffers to write from
    std::vector<asio::const_buffer> buffers;
    //! The offset at which to write
    off_t where;
#endif
    //! \constr
    io_req() { }
    //! \cconstr
    io_req(const io_req &o) : detail::io_req_impl<true>(o) { }
    //! \mconstr
    io_req(io_req &&o) noexcept : detail::io_req_impl<true>(std::move(o)) { }
    //! \cconstr
    io_req(const io_req<T> &o) : detail::io_req_impl<true>(o) { }
    //! \mconstr
    io_req(io_req<T> &&o) noexcept : detail::io_req_impl<true>(std::move(o)) { }
    //! \cassign
    io_req &operator=(const io_req &o) { static_cast<detail::io_req_impl<true>>(*this)=o; return *this; }
    //! \massign
    io_req &operator=(io_req &&o) noexcept { static_cast<detail::io_req_impl<true>>(*this)=std::move(o); return *this; }
    //! \io_req1 \param _length The number of items to transfer
    io_req(future<> _precondition, const T *v, size_t _length, off_t _where) : detail::io_req_impl<true>(std::move(_precondition), to_asio_buffers(v, _length), _where) { }
    //! \io_req1
    template<class U> io_req(future<> _precondition, const U &v, off_t _where) : detail::io_req_impl<true>(std::move(_precondition), to_asio_buffers(v), _where) { }
    //! \io_req1 \tparam N The number of items in the array
    template<class U, size_t N> io_req(future<> _precondition, const U (&v)[N], off_t _where) : detail::io_req_impl<true>(std::move(_precondition), to_asio_buffers(v), _where) { }
};
/*!
\brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes.
\ingroup io_req
*/
template<> struct io_req<void> : public detail::io_req_impl<false>
{
#ifdef DOXYGEN_SHOULD_SKIP_THIS
  //! A precondition containing an open file handle for this operation
  future<> precondition;
  //! A sequence of mutable Boost.ASIO buffers to read into
  std::vector<asio::mutable_buffer> buffers;
  //! The offset from which to read
  off_t where;
#endif
  //! \constr
  io_req() { }
  //! \cconstr
  io_req(const io_req &o) : detail::io_req_impl<false>(o) { }
  //! \mconstr
  io_req(io_req &&o) noexcept : detail::io_req_impl<false>(std::move(o)) { }
  //! \cassign
  io_req &operator=(const io_req &o) { static_cast<detail::io_req_impl<false>>(*this)=o; return *this; }
  //! \massign
  io_req &operator=(io_req &&o) noexcept { static_cast<detail::io_req_impl<false>>(*this)=std::move(o); return *this; }
  //! \io_req1 \param _length The number of items to transfer
  io_req(future<> _precondition, void *v, size_t _length, off_t _where) : detail::io_req_impl<false>(std::move(_precondition), to_asio_buffers(v, _length), _where) { }
};
/*!
\brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes.
\ingroup io_req
*/
template<> struct io_req<const void> : public detail::io_req_impl<true>
{
#ifdef DOXYGEN_SHOULD_SKIP_THIS
  //! A precondition containing an open file handle for this operation
  future<> precondition;
  //! A sequence of const Boost.ASIO buffers to write from
  std::vector<asio::const_buffer> buffers;
  //! The offset at which to write
  off_t where;
#endif
  //! \constr
  io_req() { }
  //! \cconstr
  io_req(const io_req &o) : detail::io_req_impl<true>(o) { }
  //! \mconstr
  io_req(io_req &&o) noexcept : detail::io_req_impl<true>(std::move(o)) { }
  //! \cconstr
  io_req(const io_req<void> &o) : detail::io_req_impl<true>(o) { }
  //! \mconstr
  io_req(io_req<void> &&o) noexcept : detail::io_req_impl<true>(std::move(o)) { }
  //! \cassign
  io_req &operator=(const io_req &o) { static_cast<detail::io_req_impl<true>>(*this)=o; return *this; }
  //! \massign
  io_req &operator=(io_req &&o) noexcept { static_cast<detail::io_req_impl<true>>(*this)=std::move(o); return *this; }
  //! \io_req1 \param _length The number of items to transfer
  io_req(future<> _precondition, const void *v, size_t _length, off_t _where) : detail::io_req_impl<true>(std::move(_precondition), to_asio_buffers(v, _length), _where) { }
};

namespace detail
{
  template<class T, bool is_container=detail::is_container<T>::value> struct make_io_req
  {
    typedef typename std::remove_pointer<typename std::decay<T>::type>::type _T;
    typedef io_req<_T> type;
    template<class U> type operator()(future<> _precondition, U &&v, off_t _where) const
    {
      return type(std::move(_precondition), std::forward<U>(v), _where);
    }
    template<class U> type operator()(future<> _precondition, U &&v, size_t _length, off_t _where) const
    {
      return type(std::move(_precondition), std::forward<U>(v), _length, _where);
    }
  };
  // If T is a container and that container's value_type is const, make sure we only create an asio::const_buffer
  template<class T> struct make_io_req<T, true>
  {
    typedef typename detail::is_container<T>::type container_value_type;
    static BOOST_CONSTEXPR_OR_CONST bool is_container_contents_const=std::is_const<container_value_type>::value || std::is_base_of<asio::const_buffer, container_value_type>::value;
    typedef typename std::remove_pointer<typename std::decay<T>::type>::type __T;
    typedef typename std::conditional<is_container_contents_const, typename std::add_const<__T>::type, __T>::type _T;
    typedef io_req<_T> type;
    template<class U> type operator()(future<> _precondition, U &&v, off_t _where) const
    {
      return type(std::move(_precondition), std::forward<U>(v), _where);
    }
    template<class U> type operator()(future<> _precondition, U &&v, size_t _length, off_t _where) const
    {
      return type(std::move(_precondition), std::forward<U>(v), _length, _where);
    }
  };
}
/*! \brief Convenience instantiator of a io_req, letting the compiler deduce the template specialisation to use.

\return An io_req matching the supplied parameter type.
\io_req1
\ingroup make_io_req
\qbk{distinguish, length deducing}
\qbk{
[heading Example]
[readwrite_example]
}
*/
template<class T> inline auto make_io_req(future<> _precondition, T &&v, off_t _where) -> decltype(detail::make_io_req<T>()(std::move(_precondition), std::forward<T>(v), _where))
{
  return detail::make_io_req<T>()(std::move(_precondition), std::forward<T>(v), _where);
}
/*! \brief Convenience instantiator of a io_req, letting the compiler deduce the template specialisation to use.

\return An io_req matching the supplied parameter type.
\io_req1
\ingroup make_io_req
\qbk{distinguish, length deducing}
\qbk{
[heading Example]
[readwrite_example]
}
*/
template<class T> inline io_req<const std::initializer_list<T>> make_io_req(future<> _precondition, const std::initializer_list<T> &v, off_t _where)
{
  return io_req<const std::initializer_list<T>>(std::move(_precondition), v, _where);
}
/*! \brief Convenience instantiator of a io_req, letting the compiler deduce the template specialisation to use.

\return An io_req matching the supplied parameter type.
\io_req1
\param _length The number of bytes to transfer
\ingroup make_io_req
\qbk{distinguish, length specifying}
\qbk{
[heading Example]
[readwrite_example]
}
*/
template<class T> inline auto make_io_req(future<> _precondition, T &&v, size_t _length, off_t _where) -> decltype(detail::make_io_req<T>()(std::move(_precondition), std::forward<T>(v), _length, _where))
{
  return detail::make_io_req<T>()(std::move(_precondition), std::forward<T>(v), _length, _where);
}


/*! \struct enumerate_req
\brief A convenience bundle of precondition, number of items to enumerate, item pattern match and metadata to prefetch.

You should note that shell globs must use a restricted form for portability:

* Microsoft Windows NT oddly does not specify what wildcards are permitted, but I think the documentation for the kernel
function FsRtlIsNameInExpression() is probably sound: * means zero or more characters, ? means any one character. Do not
use <, > or " as these have special MS-DOS compatibility inducing consequences. Do not use ^ as this is the Windows
wildcard escape character.

* POSIX further extends NT's wildcards with \\[seq\\] which is a subset of characters and \\[!seq\\] which is not any subset of
characters. Here a \\ is the wildcard escape character.
*/
struct enumerate_req
{
    future<> precondition;    //!< A precondition for this operation.
    size_t maxitems;             //!< The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
    bool restart;                //!< Restarts the enumeration for this open directory handle.
    path glob;                   //!< An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
    metadata_flags metadata;     //!< The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
    //! How to do deleted file elimination on Windows
    enum class filter
    {
      none,        //!< Do no filtering at all
      fastdeleted  //!< Filter out AFIO deleted files based on their filename (fast and fairly reliable)
    };
    filter filtering;            //!< Any filtering you want AFIO to do for you.
    //! \constr
    enumerate_req() : maxitems(0), restart(false), metadata(metadata_flags::None), filtering(filter::fastdeleted) { }
    /*! \brief Constructs an instance.
    
    \param _precondition The precondition for this operation.
    \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
    \param _restart Restarts the enumeration for this open directory handle.
    \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
    \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
    \param _filtering Any filtering you want AFIO to do for you.
    */
    enumerate_req(future<> _precondition, size_t _maxitems=2, bool _restart=true, path _glob=path(), metadata_flags _metadata=metadata_flags::None, filter _filtering=filter::fastdeleted) : precondition(std::move(_precondition)), maxitems(_maxitems), restart(_restart), glob(std::move(_glob)), metadata(_metadata), filtering(_filtering) { _validate(); }
    /*! \brief Constructs an instance.
    
    \param _precondition The precondition for this operation.
    \param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
    \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
    \param _restart Restarts the enumeration for this open directory handle.
    \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
    \param _filtering Any filtering you want AFIO to do for you.
    */
    enumerate_req(future<> _precondition, path _glob, size_t _maxitems=2, bool _restart=true, metadata_flags _metadata=metadata_flags::None, filter _filtering=filter::fastdeleted) : precondition(std::move(_precondition)), maxitems(_maxitems), restart(_restart), glob(std::move(_glob)), metadata(_metadata), filtering(_filtering) { _validate(); }
    /*! \brief Constructs an instance.
    
    \param _precondition The precondition for this operation.
    \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
    \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
    \param _restart Restarts the enumeration for this open directory handle.
    \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
    \param _filtering Any filtering you want AFIO to do for you.
    */
    enumerate_req(future<> _precondition, metadata_flags _metadata, size_t _maxitems=2, bool _restart=true, path _glob=path(), filter _filtering=filter::fastdeleted) : precondition(std::move(_precondition)), maxitems(_maxitems), restart(_restart), glob(std::move(_glob)), metadata(_metadata), filtering(_filtering) { _validate(); }
    //! Validates contents
    bool validate() const
    {
        if(!maxitems) return false;
        return !precondition.valid() || precondition.validate();
    }
private:
    void _validate() const
    {
#if BOOST_AFIO_VALIDATE_INPUTS
        if(!validate())
            BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
    }
};

// Undocumented deliberately
struct lock_req
{
  future<> precondition;
  enum class Type { unknown, read_lock, write_lock, unlock } type;
  off_t offset, length;
  //chrono::time_point<chrono::steady_clock> deadline;
  lock_req() : type(Type::unknown), offset(0), length(0) { }
  lock_req(future<> _precondition, Type _type=Type::write_lock) : precondition(_precondition), type(_type), offset(0), length((off_t)-1) { _validate(); }
  lock_req(future<> _precondition, std::nullptr_t) : precondition(_precondition), type(Type::unlock), offset(0), length((off_t)-1) { _validate(); }
  lock_req(future<> _precondition, Type _type, off_t _offset, off_t _length) : precondition(_precondition), type(_type), offset(_offset), length(_length) { _validate(); }
  lock_req(future<> _precondition, off_t _offset, off_t _length, Type _type=Type::write_lock) : precondition(_precondition), type(_type), offset(_offset), length(_length) { _validate(); }
  //! Validates contents
  bool validate() const
  {
      if(type==Type::unknown) return false;
      if(offset+length<offset) return false;
      return !precondition.valid() || precondition.validate();
  }
private:
  void _validate() const
  {
#if BOOST_AFIO_VALIDATE_INPUTS
      if(!validate())
          BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
  }
};



namespace detail {
    template<bool iswrite, class T> struct async_file_io_dispatcher_rwconverter
    {
        typedef detail::io_req_impl<iswrite> return_type;
        const std::vector<return_type> &operator()(const std::vector<io_req<T>> &ops) const
        {
            typedef io_req<T> reqT;
            static_assert(std::is_convertible<reqT, return_type>::value, "io_req<T> is not convertible to detail::io_req_impl<constness>");
            static_assert(sizeof(return_type)==sizeof(reqT), "io_req<T> does not have the same size as detail::io_req_impl<constness>");
            return reinterpret_cast<const std::vector<return_type> &>(ops);
        }
    };
}

#if defined(BOOST_AFIO_ENABLE_BENCHMARKING_COMPLETION) // Only really used for benchmarking
inline future<> dispatcher::completion(const future<> &req, const std::pair<async_op_flags, dispatcher::completion_t *> &callback)
{
    std::vector<future<>> r;
    std::vector<std::pair<async_op_flags, dispatcher::completion_t *>> i;
    r.reserve(1); i.reserve(1);
    r.push_back(req);
    i.push_back(callback);
    auto ret(std::move(completion(r, i).front()));
    return ret;
}
#endif
inline future<> dispatcher::completion(const future<> &req, const std::pair<async_op_flags, std::function<dispatcher::completion_t>> &callback)
{
    std::vector<future<>> r;
    std::vector<std::pair<async_op_flags, std::function<dispatcher::completion_t>>> i;
    r.reserve(1); i.reserve(1);
    r.push_back(req);
    i.push_back(callback);
    auto ret(std::move(completion(r, i).front()));
    return ret;
}
namespace detail {
    template<class tasktype> std::pair<bool, handle_ptr> doCall(size_t, future<> _, std::shared_ptr<tasktype> c)
    {
        (*c)();
        return std::make_pair(true, _.get_handle(true));
    }
}
template<class R> inline std::vector<future<R>> dispatcher::call(const std::vector<future<>> &ops, const std::vector<std::function<R()>> &callables)
{
    typedef packaged_task<R()> tasktype;
    std::vector<stl_future<R>> retfutures;
    std::vector<std::pair<async_op_flags, std::function<completion_t>>> callbacks;
    retfutures.reserve(callables.size());
    callbacks.reserve(callables.size());
    
    for(auto &t: callables)
    {
        std::shared_ptr<tasktype> c(std::make_shared<tasktype>(std::function<R()>(t)));
        retfutures.push_back(c->get_future());
        callbacks.push_back(std::make_pair(async_op_flags::none, std::bind(&detail::doCall<tasktype>, std::placeholders::_1, std::placeholders::_2, std::move(c))));
    }
    auto _ret(completion(ops, callbacks));
    std::vector<future<R>> ret;
    ret.reserve(_ret.size());
    for (size_t n = 0; n < _ret.size(); n++)
      ret.push_back(future<R>(std::move(_ret[n]), std::move(retfutures[n])));
    return ret;
}
template<class R> inline future<R> dispatcher::call(const future<> &req, std::function<R()> callback)
{
    std::vector<future<>> i;
    std::vector<std::function<R()>> c;
    i.reserve(1); c.reserve(1);
    i.push_back(req);
    c.push_back(std::move(callback));
    auto ret(std::move(call(i, c).front()));
    return ret;
}

#ifndef DOXYGEN_SHOULD_SKIP_THIS
template<class C, class... Args> inline future<typename detail::vs2013_variadic_overload_resolution_workaround<C, Args...>::type> dispatcher::call(const future<> &req, C callback, Args... args)
#else
template<class C, class... Args> inline future<typename std::result_of<C(Args...)>::type> dispatcher::call(const future<> &req, C callback, Args... args)
#endif
{
    typedef typename std::result_of<C(Args...)>::type rettype;
    return call(req, std::function<rettype()>(std::bind<rettype>(callback, args...)));
}

inline future<> dispatcher::adopt(handle_ptr h)
{
    std::vector<handle_ptr> i;
    i.reserve(1);
    i.push_back(std::move(h));
    auto ret(std::move(adopt(i).front()));
    return ret;
}
inline future<> dispatcher::dir(const path_req &req)
{
    std::vector<path_req> i;
    i.reserve(1);
    i.push_back(req);
    auto ret(std::move(dir(i).front()));
    return ret;
}
inline future<> dispatcher::rmdir(const path_req &req)
{
    std::vector<path_req> i;
    i.reserve(1);
    i.push_back(req);
    auto ret(std::move(rmdir(i).front()));
    return ret;
}
inline future<> dispatcher::file(const path_req &req)
{
    std::vector<path_req> i;
    i.reserve(1);
    i.push_back(req);
    auto ret(std::move(file(i).front()));
    return ret;
}
inline future<> dispatcher::rmfile(const path_req &req)
{
    std::vector<path_req> i;
    i.reserve(1);
    i.push_back(req);
    auto ret(std::move(rmfile(i).front()));
    return ret;
}
inline future<> dispatcher::symlink(const path_req &req, const future<> &target)
{
    std::vector<path_req> i(1, req);
    std::vector<future<>> t(1, target);
    auto ret(std::move(symlink(i, t).front()));
    return ret;
}
inline future<> dispatcher::rmsymlink(const path_req &req)
{
    std::vector<path_req> i;
    i.reserve(1);
    i.push_back(req);
    auto ret(std::move(rmsymlink(i).front()));
    return ret;
}
inline future<> dispatcher::sync(const future<> &req)
{
    std::vector<future<>> i;
    i.reserve(1);
    i.push_back(req);
    auto ret(std::move(sync(i).front()));
    return ret;
}
inline future<> dispatcher::zero(const future<> &req, const std::vector<std::pair<off_t, off_t>> &ranges)
{
    std::vector<future<>> i;
    std::vector<std::vector<std::pair<off_t, off_t>>> r;
    i.reserve(1);
    i.push_back(req);
    r.reserve(1);
    r.push_back(ranges);
    auto ret(std::move(zero(i, r).front()));
    return ret;
}
inline future<> dispatcher::close(const future<> &req)
{
    std::vector<future<>> i;
    i.reserve(1);
    i.push_back(req);
    auto ret(std::move(close(i).front()));
    return ret;
}
#ifndef DOXYGEN_SHOULD_SKIP_THIS
inline future<> dispatcher::read(const detail::io_req_impl<false> &req)
{
    std::vector<detail::io_req_impl<false>> i;
    i.reserve(1);
    i.push_back(req);
    auto ret(std::move(read(i).front()));
    return ret;
}
inline future<> dispatcher::write(const detail::io_req_impl<true> &req)
{
    std::vector<detail::io_req_impl<true>> i;
    i.reserve(1);
    i.push_back(req);
    auto ret(std::move(write(i).front()));
    return ret;
}
#endif
template<class T> inline std::vector<future<>> dispatcher::read(const std::vector<io_req<T>> &ops)
{
    return read(detail::async_file_io_dispatcher_rwconverter<false, T>()(ops));
}
template<class T> inline std::vector<future<>> dispatcher::write(const std::vector<io_req<T>> &ops)
{
    return write(detail::async_file_io_dispatcher_rwconverter<true, T>()(ops));
}
inline future<> dispatcher::truncate(const future<> &op, off_t newsize)
{
    std::vector<future<>> o;
    std::vector<off_t> i;
    o.reserve(1);
    o.push_back(op);
    i.reserve(1);
    i.push_back(newsize);
    auto ret(std::move(truncate(o, i).front()));
    return ret;
}
inline future<std::pair<std::vector<directory_entry>, bool>> dispatcher::enumerate(const enumerate_req &req)
{
    std::vector<enumerate_req> i;
    i.reserve(1);
    i.push_back(req);
    auto ret(std::move(enumerate(i).front()));
    return ret;
}
inline future<std::vector<std::pair<off_t, off_t>>> dispatcher::extents(const future<> &op)
{
    std::vector<future<>> o;
    o.reserve(1);
    o.push_back(op);
    auto ret(std::move(extents(o).front()));
    return ret;
}
inline future<statfs_t> dispatcher::statfs(const future<> &op, const fs_metadata_flags &req)
{
  std::vector<future<>> o;
  std::vector<fs_metadata_flags> i;
  o.reserve(1);
  o.push_back(op);
  i.reserve(1);
  i.push_back(req);
  auto ret(std::move(statfs(o, i).front()));
  return ret;
}
inline future<> dispatcher::depends(future<> precondition, future<> op)
{
    std::pair<async_op_flags, std::function<dispatcher::completion_t>> callback(std::make_pair(async_op_flags::immediate,
    [BOOST_AFIO_LAMBDA_MOVE_CAPTURE(op)](size_t, future<>) { return std::make_pair(true, op.get_handle()); }));
    std::vector<future<>> r;
    std::vector<std::pair<async_op_flags, std::function<dispatcher::completion_t>>> i;
    r.reserve(1); i.reserve(1);
    r.push_back(precondition);
    i.push_back(std::move(callback));
    auto ret(std::move(completion(r, i).front()));
    return ret;
}

namespace detail
{
  template<class T> struct async_dir
  {
    T path;
    file_flags flags;
    async_dir(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { }
    future<> operator()(future<> f=future<>())
    {
      dispatcher *dispatcher = f.parent();
      path_req req(!dispatcher ? (
        dispatcher = current_dispatcher().get(),
        path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
        ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
#if BOOST_AFIO_VALIDATE_INPUTS
      if (!req.validate())
        BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
      auto ret(std::move(dispatcher->dir(std::vector<path_req>(1, std::move(req))).front()));
      return ret;
    }
  };
  template<class T> struct async_rmdir
  {
    T path;
    file_flags flags;
    async_rmdir(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { }
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      path_req req(!dispatcher ? (
        dispatcher = current_dispatcher().get(),
        path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
        ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
#if BOOST_AFIO_VALIDATE_INPUTS
      if (!req.validate())
        BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
      auto ret(std::move(dispatcher->rmdir(std::vector<path_req>(1, std::move(req))).front()));
      return ret;
    }
  };
  template<class T> struct async_file
  {
    T path;
    file_flags flags;
    async_file(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { }
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      path_req req(!dispatcher ? (
        dispatcher = current_dispatcher().get(),
        path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
        ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
#if BOOST_AFIO_VALIDATE_INPUTS
      if (!req.validate())
        BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
      auto ret(std::move(dispatcher->file(std::vector<path_req>(1, std::move(req))).front()));
      return ret;
    }
  };
  template<class T> struct async_rmfile
  {
    T path;
    file_flags flags;
    async_rmfile(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { }
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      path_req req(!dispatcher ? (
        dispatcher = current_dispatcher().get(),
        path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
        ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
#if BOOST_AFIO_VALIDATE_INPUTS
      if (!req.validate())
        BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
      auto ret(std::move(dispatcher->rmfile(std::vector<path_req>(1, std::move(req))).front()));
      return ret;
    }
  };
  template<class T> struct async_symlink
  {
    T path;
    file_flags flags;
    future<> target;
    async_symlink(T _path, file_flags _flags, future<> _target) : path(std::move(_path)), flags(_flags), target(std::move(_target)) { }
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      path_req req(!dispatcher ? (
        dispatcher = current_dispatcher().get(),
        path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
        ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
#if BOOST_AFIO_VALIDATE_INPUTS
      if (!req.validate())
        BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
      auto ret(std::move(dispatcher->symlink(std::vector<path_req>(1, std::move(req)), std::vector<future<>>(1, std::move(target))).front()));
      return ret;
    }
  };
  template<class T> struct async_rmsymlink
  {
    T path;
    file_flags flags;
    async_rmsymlink(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { }
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      path_req req(!dispatcher ? (
        dispatcher = current_dispatcher().get(),
        path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
        ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
#if BOOST_AFIO_VALIDATE_INPUTS
      if (!req.validate())
        BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
      auto ret(std::move(dispatcher->rmsymlink(std::vector<path_req>(1, std::move(req))).front()));
      return ret;
    }
  };
  struct async_sync
  {
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      if (!dispatcher)
        dispatcher = current_dispatcher().get();
      auto ret(std::move(dispatcher->sync(std::vector<future<>>(1, std::move(f))).front()));
      return ret;
    }
  };
  struct async_close
  {
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      if (!dispatcher)
        dispatcher = current_dispatcher().get();
      auto ret(std::move(dispatcher->close(std::vector<future<>>(1, std::move(f))).front()));
      return ret;
    }
  };
  struct async_read
  {
    io_req_impl<false> req;
    template<class U> async_read(U &&v, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward<U>(v), _where)) { }
    template<class U> async_read(U &&v, size_t _length, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward<U>(v), _length, _where)) { }
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      if (!dispatcher)
        dispatcher = current_dispatcher().get();
      req.precondition = f;
#if BOOST_AFIO_VALIDATE_INPUTS
      if (!req.validate())
        BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
      auto ret(std::move(dispatcher->read(std::vector<io_req_impl<false>>(1, std::move(req))).front()));
      return ret;
    }
  };
  struct async_write
  {
    io_req_impl<true> req;
    template<class U> async_write(U &&v, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward<U>(v), _where)) { }
    template<class U> async_write(U &&v, size_t _length, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward<U>(v), _length, _where)) { }
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      if (!dispatcher)
        dispatcher = current_dispatcher().get();
      req.precondition = f;
#if BOOST_AFIO_VALIDATE_INPUTS
      if (!req.validate())
        BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
      auto ret(std::move(dispatcher->write(std::vector<io_req_impl<true>>(1, std::move(req))).front()));
      return ret;
    }
  };
  struct async_truncate
  {
    off_t _size;
    async_truncate(off_t size) : _size(size) { }
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      if (!dispatcher)
        dispatcher = current_dispatcher().get();
      auto ret(std::move(dispatcher->truncate(std::vector<future<>>(1, std::move(f)), std::vector<off_t>(1, _size)).front()));
      return ret;
    }
  };
  struct async_enumerate
  {
    size_t maxitems;
    bool restart;
    path glob;
    metadata_flags metadata;
    enumerate_req::filter filtering;
    async_enumerate(size_t _maxitems, bool _restart, path _glob, metadata_flags _metadata, enumerate_req::filter _filtering) : maxitems(_maxitems), restart(_restart), glob(_glob), metadata(_metadata), filtering(_filtering) { }
    future<std::pair<std::vector<directory_entry>, bool>> operator()(future<> f = future<>())
    {
      enumerate_req req(std::move(f), maxitems, restart, std::move(glob), metadata, filtering);
      dispatcher *dispatcher = f.parent();
      if (!dispatcher)
        dispatcher = current_dispatcher().get();
#if BOOST_AFIO_VALIDATE_INPUTS
      if (!req.validate())
        BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
#endif
      auto ret(std::move(dispatcher->enumerate(std::vector<enumerate_req>(1, std::move(req))).front()));
      return ret;
    }
  };
  struct async_zero
  {
    std::vector<std::pair<off_t, off_t>> ranges;
    async_zero(std::vector<std::pair<off_t, off_t>> _ranges) : ranges(_ranges) { }
    future<> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      if (!dispatcher)
        dispatcher = current_dispatcher().get();
      auto ret(std::move(dispatcher->zero(std::vector<future<>>(1, std::move(f)), std::vector<std::vector<std::pair<off_t, off_t>>>(1, std::move(ranges))).front()));
      return ret;
    }
  };
  struct async_extents
  {
    future<std::vector<std::pair<off_t, off_t>>> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      if (!dispatcher)
        dispatcher = current_dispatcher().get();
      auto ret(std::move(dispatcher->extents(std::vector<future<>>(1, std::move(f))).front()));
      return ret;
    }
  };
  struct async_statfs
  {
    fs_metadata_flags req;
    async_statfs(fs_metadata_flags _req) : req(_req) { }
    future<statfs_t> operator()(future<> f = future<>())
    {
      dispatcher *dispatcher = f.parent();
      if (!dispatcher)
        dispatcher = current_dispatcher().get();
      auto ret(std::move(dispatcher->statfs(std::vector<future<>>(1, std::move(f)), std::vector<fs_metadata_flags>(1, req)).front()));
      return ret;
    }
  };
  template<class T> struct _is_not_handle : public std::true_type { };
  template<class T> struct _is_not_handle<future<T>> : public std::false_type { };
  template<> struct _is_not_handle<handle_ptr> : public std::false_type { };
  template<class T> struct is_not_handle : public _is_not_handle<typename std::decay<T>::type> { };
}

/*! \brief Asynchronous directory creation and open after an optional precondition.

\docs_dir
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\return A future<void>
\param _precondition The precondition to use.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup dir
\qbk{distinguish, relative}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T> inline future<> async_dir(future<> _precondition, T _path, file_flags _flags = file_flags::none)
{
  return detail::async_dir<T>(std::move(_path), _flags)(std::move(_precondition));
}
/*! \brief Asynchronous directory creation and open after an optional precondition.

\docs_dir
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\return A future<void>
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup dir
\qbk{distinguish, absolute}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename=typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_dir(T _path, file_flags _flags = file_flags::none)
{
  return detail::async_dir<T>(std::move(_path), _flags)(future<>());
}
/*! \brief Synchronous directory creation and open after an optional precondition.

\docs_dir
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the directory.
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup dir
\qbk{distinguish, relative throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T> inline handle_ptr dir(future<> _precondition, T _path, file_flags _flags = file_flags::none)
{
  return detail::async_dir<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous directory creation and open after an optional precondition.

\docs_dir
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the directory.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup dir
\qbk{distinguish, absolute throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr dir(T _path, file_flags _flags = file_flags::none)
{
  return detail::async_dir<T>(std::move(_path), _flags)(future<>()).get_handle();
}
/*! \brief Synchronous directory creation and open after an optional precondition.

\docs_dir
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the directory.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup dir
\qbk{distinguish, relative non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T> inline handle_ptr dir(error_code &_ec, future<> _precondition, T _path, file_flags _flags = file_flags::none)
{
  return detail::async_dir<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec);
}
/*! \brief Synchronous directory creation and open after an optional precondition.

\docs_dir
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the directory.
\param _ec Error code to set.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup dir
\qbk{distinguish, absolute non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr dir(error_code &_ec, T _path, file_flags _flags = file_flags::none)
{
  return detail::async_dir<T>(std::move(_path), _flags)(future<>()).get_handle(_ec);
}

/*! \brief Asynchronous directory deletion after an optional precondition.

\docs_rmdir
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A future<void>
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup rmdir
\qbk{distinguish, relative}
\raceguarantees{
[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T=path> inline future<> async_rmdir(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
{
  return detail::async_rmdir<T>(std::move(_path), _flags)(std::move(_precondition));
}
/*! \brief Asynchronous directory deletion after an optional precondition.

\docs_rmdir
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A future<void>
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup rmdir
\qbk{distinguish, absolute}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_rmdir(T _path, file_flags _flags = file_flags::none)
{
  return detail::async_rmdir<T>(std::move(_path), _flags)(future<>());
}
/*! \brief Synchronous directory deletion after an optional precondition.

\docs_rmdir
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup rmdir
\qbk{distinguish, relative throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T=path> inline void rmdir(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
{
  detail::async_rmdir<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous directory deletion after an optional precondition.

\docs_rmdir
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup rmdir
\qbk{distinguish, absolute throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmdir(T _path, file_flags _flags = file_flags::none)
{
  detail::async_rmdir<T>(std::move(_path), _flags)(future<>()).get_handle();
}
/*! \brief Synchronous directory deletion after an optional precondition.

\docs_rmdir
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup rmdir
\qbk{distinguish, relative non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T=path> inline void rmdir(error_code &_ec, future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
{
  detail::async_rmdir<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec);
}
/*! \brief Synchronous directory deletion after an optional precondition.

\docs_rmdir
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\param _ec Error code to set.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup rmdir
\qbk{distinguish, absolute non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmdir(error_code &_ec, T _path, file_flags _flags = file_flags::none)
{
  detail::async_rmdir<T>(std::move(_path), _flags)(future<>()).get_handle(_ec);
}

/*! \brief Asynchronous file creation and open after an optional precondition.

\docs_file
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A future<void>
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup file
\qbk{distinguish, relative}
\raceguarantees{
[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T> inline future<> async_file(future<> _precondition, T _path, file_flags _flags = file_flags::none)
{
  return detail::async_file<T>(std::move(_path), _flags)(std::move(_precondition));
}
/*! \brief Asynchronous file creation and open after an optional precondition.

\docs_file
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A future<void>
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup file
\qbk{distinguish, absolute}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_file(T _path, file_flags _flags = file_flags::none)
{
  return detail::async_file<T>(std::move(_path), _flags)(future<>());
}
/*! \brief Synchronous file creation and open after an optional precondition.

\docs_file
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the file.
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup file
\qbk{distinguish, relative throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T> inline handle_ptr file(future<> _precondition, T _path, file_flags _flags = file_flags::none)
{
  return detail::async_file<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous file creation and open after an optional precondition.

\docs_file
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the file.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup file
\qbk{distinguish, absolute throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr file(T _path, file_flags _flags = file_flags::none)
{
  return detail::async_file<T>(std::move(_path), _flags)(future<>()).get_handle();
}
/*! \brief Synchronous file creation and open after an optional precondition.

\docs_file
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the file.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup file
\qbk{distinguish, relative non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T> inline handle_ptr file(error_code &_ec, future<> _precondition, T _path, file_flags _flags = file_flags::none)
{
  return detail::async_file<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec);
}
/*! \brief Synchronous file creation and open after an optional precondition.

\docs_file
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the file.
\param _ec Error code to set.
\param _path The filing system path to use.
\param _flags The flags to use.
\ingroup file
\qbk{distinguish, absolute non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr file(error_code &_ec, T _path, file_flags _flags = file_flags::none)
{
  return detail::async_file<T>(std::move(_path), _flags)(future<>()).get_handle(_ec);
}

/*! \brief Asynchronous file deletion after an optional precondition.

\docs_rmfile
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\return A future<void>
\param _precondition The precondition to use.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmfile
\qbk{distinguish, relative}
\raceguarantees{
[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T=path> inline future<> async_rmfile(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
{
  return detail::async_rmfile<T>(std::move(_path), _flags)(std::move(_precondition));
}
/*! \brief Asynchronous file deletion after an optional precondition.

\docs_rmfile
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\return A future<void>
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmfile
\qbk{distinguish, absolute}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_rmfile(T _path, file_flags _flags = file_flags::none)
{
  return detail::async_rmfile<T>(std::move(_path), _flags)(future<>());
}
/*! \brief Synchronous file deletion after an optional precondition.

\docs_rmfile
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\param _precondition The precondition to use.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmfile
\qbk{distinguish, relative throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T=path> inline void rmfile(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
{
  detail::async_rmfile<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous file deletion after an optional precondition.

\docs_rmfile
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmfile
\qbk{distinguish, absolute throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmfile(T _path, file_flags _flags = file_flags::none)
{
  detail::async_rmfile<T>(std::move(_path), _flags)(future<>()).get_handle();
}
/*! \brief Synchronous file deletion after an optional precondition.

\docs_rmfile
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmfile
\qbk{distinguish, relative non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T=path> inline void rmfile(error_code &_ec, future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
{
  detail::async_rmfile<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec);
}
/*! \brief Synchronous file deletion after an optional precondition.

\docs_rmfile
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\param _ec Error code to set.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmfile
\qbk{distinguish, absolute non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmfile(error_code &_ec, T _path, file_flags _flags = file_flags::none)
{
  detail::async_rmfile<T>(std::move(_path), _flags)(future<>()).get_handle(_ec);
}


/*! \brief Asynchronous symlink creation and open after a precondition.

\docs_symlink
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A future<void>
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _target The item to link to if creating.
\param _flags The flags to use.
\ingroup symlink
\qbk{distinguish, relative}
\raceguarantees{
[raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T> inline future<> async_symlink(future<> _precondition, T _path, future<> _target=future<>(), file_flags _flags = file_flags::none)
{
  return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(std::move(_precondition));
}
/*! \brief Asynchronous symlink creation and open after a precondition.

\docs_symlink
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A future<void>
\param _path The filing system path to use.
\param _target The item to link to if creating.
\param _flags The flags to use.
\ingroup symlink
\qbk{distinguish, absolute}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_symlink(T _path, future<> _target=future<>(), file_flags _flags = file_flags::none)
{
  return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(future<>());
}
/*! \brief Synchronous symlink creation and open after a precondition..

\docs_symlink
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the symlink.
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _target The item to link to if creating.
\param _flags The flags to use.
\ingroup symlink
\qbk{distinguish, relative throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T> inline handle_ptr symlink(future<> _precondition, T _path, future<> _target = future<>(), file_flags _flags = file_flags::none)
{
  return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous symlink creation and open after a precondition.

\docs_symlink
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the symlink.
\param _path The filing system path to use.
\param _target The item to link to if creating.
\param _flags The flags to use.
\ingroup symlink
\qbk{distinguish, absolute throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr symlink(T _path, future<> _target = future<>(), file_flags _flags = file_flags::none)
{
  return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(future<>()).get_handle();
}
/*! \brief Synchronous symlink creation and open after a precondition.

\docs_symlink
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the symlink.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param _path The filing system path to use.
\param _target The item to link to if creating.
\param _flags The flags to use.
\ingroup symlink
\qbk{distinguish, relative non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T> inline handle_ptr symlink(error_code &_ec, future<> _precondition, T _path, future<> _target = future<>(), file_flags _flags = file_flags::none)
{
  return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(std::move(_precondition)).get_handle(_ec);
}
/*! \brief Synchronous symlink creation and open after a precondition.

\docs_symlink
\ntkernelnamespacenote

\tparam "class T" The type of path to use.
\return A handle to the symlink.
\param _ec Error code to set.
\param _path The filing system path to use.
\param _target The item to link to if creating.
\param _flags The flags to use.
\ingroup symlink
\qbk{distinguish, absolute non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr symlink(error_code &_ec, T _path, future<> _target = future<>(), file_flags _flags = file_flags::none)
{
  return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(future<>()).get_handle(_ec);
}

/*! \brief Asynchronous symlink deletion after an optional precondition.

\docs_rmsymlink
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\return A future<void>
\param _precondition The precondition to use.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmsymlink
\qbk{distinguish, relative}
\raceguarantees{
[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T=path> inline future<> async_rmsymlink(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
{
  return detail::async_rmsymlink<T>(std::move(_path), _flags)(std::move(_precondition));
}
/*! \brief Asynchronous symlink deletion after an optional precondition.

\docs_rmsymlink
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\return A future<void>
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmsymlink
\qbk{distinguish, absolute}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_rmsymlink(T _path, file_flags _flags = file_flags::none)
{
  return detail::async_rmsymlink<T>(std::move(_path), _flags)(future<>());
}
/*! \brief Synchronous symlink deletion after an optional precondition.

\docs_rmsymlink
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\param _precondition The precondition to use.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmsymlink
\qbk{distinguish, relative throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T=path> inline void rmsymlink(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
{
  detail::async_rmsymlink<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous symlink deletion after an optional precondition.

\docs_rmsymlink
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmsymlink
\qbk{distinguish, absolute throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmsymlink(T _path, file_flags _flags = file_flags::none)
{
  detail::async_rmsymlink<T>(std::move(_path), _flags)(future<>()).get_handle();
}
/*! \brief Synchronous symlink deletion after an optional precondition.

\docs_rmsymlink
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmsymlink
\qbk{distinguish, relative non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
[raceguarantee OS X..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T=path> inline void rmsymlink(error_code &_ec, future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
{
  detail::async_rmsymlink<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec);
}
/*! \brief Synchronous symlink deletion after an optional precondition.

\docs_rmsymlink
\ntkernelnamespacenote

\tparam "class T" The type of path to be used.
\param _ec Error code to set.
\param _path The filing system path to be used.
\param _flags The flags to be used.
\ingroup rmsymlink
\qbk{distinguish, absolute non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmsymlink(error_code &_ec, T _path, file_flags _flags = file_flags::none)
{
  detail::async_rmsymlink<T>(std::move(_path), _flags)(future<>()).get_handle(_ec);
}


/*! \brief Asynchronous content synchronisation with physical storage after a preceding operation.

\docs_sync

\return A future<void>
\param _precondition The precondition to use.
\ingroup sync
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).}
\exceptionmodelfree
\qexample{readwrite_example}
*/
inline future<> async_sync(future<> _precondition)
{
  return detail::async_sync()(std::move(_precondition));
}
/*! \brief Synchronous content synchronisation with physical storage after a preceding operation.

\docs_sync

\param _precondition The precondition to use.
\ingroup sync
\qbk{distinguish, throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).}
\exceptionmodelfree
\qexample{readwrite_example}
*/
inline void sync(future<> _precondition)
{
  detail::async_sync()(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous content synchronisation with physical storage after a preceding operation.

\docs_sync
\param _ec Error code to set.
\param _precondition The precondition to use.
\ingroup sync
\qbk{distinguish, non throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).}
\exceptionmodelfree
\qexample{readwrite_example}
*/
inline void sync(error_code &_ec, future<> _precondition)
{
  detail::async_sync()(std::move(_precondition)).get_handle(_ec);
}


/*! \brief Asynchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation.

\docs_zero

\return A future<void>
\param _precondition The precondition to use.
\param ranges A sequence of extents to zero and deallocate
\ingroup zero
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.}
\exceptionmodelfree
\qexample{extents_example}
*/
inline future<> async_zero(future<> _precondition, std::vector<std::pair<off_t, off_t>> ranges)
{
  return detail::async_zero(std::move(ranges))(std::move(_precondition));
}
/*! \brief Synchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation.

\docs_zero

\param _precondition The precondition to use.
\param ranges A sequence of extents to zero and deallocate
\ingroup zero
\qbk{distinguish, throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.}
\exceptionmodelfree
\qexample{extents_example}
*/
inline void zero(future<> _precondition, std::vector<std::pair<off_t, off_t>> ranges)
{
  detail::async_zero(std::move(ranges))(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation.

\docs_zero

\param _ec Error code to set.
\param _precondition The precondition to use.
\param ranges A sequence of extents to zero and deallocate
\ingroup zero
\qbk{distinguish, non throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.}
\exceptionmodelfree
\qexample{extents_example}
*/
inline void zero(error_code &_ec, future<> _precondition, std::vector<std::pair<off_t, off_t>> ranges)
{
  detail::async_zero(std::move(ranges))(std::move(_precondition)).get_handle(_ec);
}


/*! \brief Asynchronous file or directory handle close after a preceding operation.

\docs_close

\return A future<void>
\param _precondition The precondition to use.
\ingroup close
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
inline future<> async_close(future<> _precondition)
{
  return detail::async_close()(std::move(_precondition));
}
/*! \brief Synchronous file or directory handle close after a preceding operation.

\docs_close

\param _precondition The precondition to use.
\ingroup close
\qbk{distinguish, throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
inline void close(future<> _precondition)
{
  detail::async_close()(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous file or directory handle close after a preceding operation.

\docs_close

\param _ec Error code to set.
\param _precondition The precondition to use.
\ingroup close
\qbk{distinguish, non throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.}
\exceptionmodelfree
\qexample{filedir_example}
*/
inline void close(error_code &_ec, future<> _precondition)
{
  detail::async_close()(std::move(_precondition)).get_handle(_ec);
}


/*! \brief Asynchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.

\docs_read
\direct_io_note

\tparam "class T" Any type.
\return A future<void>
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _where The file offset to do the i/o
\ingroup read
\qbk{distinguish, length deducing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline future<> async_read(future<> _precondition, T &&v, off_t _where)
{
  return detail::async_read(std::forward<T>(v), _where)(std::move(_precondition));
}
/*! \brief Asynchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.

\docs_read
\direct_io_note

\tparam "class T" Any type.
\return A future<void>
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _length The length of the item
\param _where The file offset to do the i/o
\ingroup read
\qbk{distinguish, length specifying}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline future<> async_read(future<> _precondition, T &&v, size_t _length, off_t _where)
{
  return detail::async_read(std::forward<T>(v), _length, _where)(std::move(_precondition));
}
/*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.

\docs_read
\direct_io_note

\tparam "class T" Any type.
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _where The file offset to do the i/o
\ingroup read
\qbk{distinguish, length deducing throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline void read(future<> _precondition, T &&v, off_t _where)
{
  detail::async_read(std::forward<T>(v), _where)(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.

\docs_read
\direct_io_note

\tparam "class T" Any type.
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _length The length of the item
\param _where The file offset to do the i/o
\ingroup read
\qbk{distinguish, length specifying throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline void read(future<> _precondition, T &&v, size_t _length, off_t _where)
{
  detail::async_read(std::forward<T>(v), _length, _where)(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.

\docs_read
\direct_io_note

\tparam "class T" Any type.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _where The file offset to do the i/o
\ingroup read
\qbk{distinguish, length deducing non throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline void read(error_code &_ec, future<> _precondition, T &&v, off_t _where)
{
  detail::async_read(std::forward<T>(v), _where)(std::move(_precondition)).get_handle(_ec);
}
/*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.

\docs_read
\direct_io_note

\tparam "class T" Any type.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _length The length of the item
\param _where The file offset to do the i/o
\ingroup read
\qbk{distinguish, length specifying non throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline void read(error_code &_ec, future<> _precondition, T &&v, size_t _length, off_t _where)
{
  detail::async_read(std::forward<T>(v), _length, _where)(std::move(_precondition)).get_handle(_ec);
}


/*! \brief Asynchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.

\docs_write
\direct_io_note

\tparam "class T" Any type.
\return A future<void>
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _where The file offset to do the i/o
\ingroup write
\qbk{distinguish, length deducing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline future<> async_write(future<> _precondition, T &&v, off_t _where)
{
  return detail::async_write(std::forward<T>(v), _where)(std::move(_precondition));
}
/*! \brief Asynchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.

\docs_write
\direct_io_note

\tparam "class T" Any type.
\return A future<void>
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _length The length of the item
\param _where The file offset to do the i/o
\ingroup write
\qbk{distinguish, length specifying}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline future<> async_write(future<> _precondition, T &&v, size_t _length, off_t _where)
{
  return detail::async_write(std::forward<T>(v), _length, _where)(std::move(_precondition));
}
/*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.

\docs_write
\direct_io_note

\tparam "class T" Any type.
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _where The file offset to do the i/o
\ingroup write
\qbk{distinguish, length deducing throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline void write(future<> _precondition, T &&v, off_t _where)
{
  detail::async_write(std::forward<T>(v), _where)(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.

\docs_write
\direct_io_note

\tparam "class T" Any type.
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _length The length of the item
\param _where The file offset to do the i/o
\ingroup write
\qbk{distinguish, length specifying throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline void write(future<> _precondition, T &&v, size_t _length, off_t _where)
{
  detail::async_write(std::forward<T>(v), _length, _where)(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.

\docs_write
\direct_io_note

\tparam "class T" Any type.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _where The file offset to do the i/o
\ingroup write
\qbk{distinguish, length deducing non throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline void write(error_code &_ec, future<> _precondition, T &&v, off_t _where)
{
  detail::async_write(std::forward<T>(v), _where)(std::move(_precondition)).get_handle(_ec);
}
/*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.

\docs_write
\direct_io_note

\tparam "class T" Any type.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param v Some item understood by `to_asio_buffers()`
\param _length The length of the item
\param _where The file offset to do the i/o
\ingroup write
\qbk{distinguish, length specifying non throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
template<class T> inline void write(error_code &_ec, future<> _precondition, T &&v, size_t _length, off_t _where)
{
  detail::async_write(std::forward<T>(v), _length, _where)(std::move(_precondition)).get_handle(_ec);
}


/*! \brief Asynchronous file length truncation after a preceding operation.

\docs_truncate

\return A future<void>
\param _precondition The precondition to use.
\param newsize The new size for the file.
\ingroup truncate
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
inline future<> async_truncate(future<> _precondition, off_t newsize)
{
  return detail::async_truncate(newsize)(std::move(_precondition));
}
/*! \brief Synchronous file length truncation after a preceding operation.

\docs_truncate

\param _precondition The precondition to use.
\param newsize The new size for the file.
\ingroup truncate
\qbk{distinguish, throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
inline void truncate(future<> _precondition, off_t newsize)
{
  auto h=detail::async_truncate(newsize)(std::move(_precondition)).get_handle();
}
/*! \brief Synchronous file length truncation after a preceding operation.

\docs_truncate

\param _ec Error code to set.
\param _precondition The precondition to use.
\param newsize The new size for the file.
\ingroup truncate
\qbk{distinguish, non throwing}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.}
\exceptionmodelfree
\qexample{readwrite_example}
*/
inline void truncate(error_code &_ec, future<> _precondition, off_t newsize)
{
  auto h=detail::async_truncate(newsize)(std::move(_precondition)).get_handle(_ec);
}


/*! \brief Asynchronous directory enumeration after a preceding operation.

\docs_enumerate

\return A `future<std::pair<std::vector<directory_entry>, bool>>`
\param _precondition The precondition to use.
\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
\param _restart Restarts the enumeration for this open directory handle.
\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
\param _filtering Any filtering you want AFIO to do for you.
\ingroup enumerate
\qbk{distinguish, maxitems first}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
comparing inodes for equivalence to a direntry() won't help you.]
[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
birthtim, sparse, compressed.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
\exceptionmodelfree
\qexample{enumerate_example}
*/
inline future<std::pair<std::vector<directory_entry>, bool>> async_enumerate(future<> _precondition, size_t _maxitems = 2, bool _restart = true, path _glob = path(),
  metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
{
  return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition));
}
/*! \brief Synchronous directory enumeration after a preceding operation.

\docs_enumerate

\return A vector of results and a bool indicating if there is more.
\param _precondition The precondition to use.
\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
\param _restart Restarts the enumeration for this open directory handle.
\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
\param _filtering Any filtering you want AFIO to do for you.
\ingroup enumerate
\qbk{distinguish, max items first throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
comparing inodes for equivalence to a direntry() won't help you.]
[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
birthtim, sparse, compressed.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
\exceptionmodelfree
\qexample{enumerate_example}
*/
inline std::pair<std::vector<directory_entry>, bool> enumerate(future<> _precondition, size_t _maxitems = 2, bool _restart = true, path _glob = path(),
  metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
{
  return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)).get();
}
/*! \brief Synchronous directory enumeration after a preceding operation.

\docs_enumerate

\return A vector of results and a bool indicating if there is more.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
\param _restart Restarts the enumeration for this open directory handle.
\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
\param _filtering Any filtering you want AFIO to do for you.
\ingroup enumerate
\qbk{distinguish, max items first non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
comparing inodes for equivalence to a direntry() won't help you.]
[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
birthtim, sparse, compressed.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
\exceptionmodelfree
\qexample{enumerate_example}
*/
inline std::pair<std::vector<directory_entry>, bool> enumerate(error_code &_ec, future<> _precondition, size_t _maxitems = 2, bool _restart = true, path _glob = path(),
  metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
{
  auto ret= detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition));
  if(!(_ec=ret.get_error()))
    return ret.get();
  return std::pair<std::vector<directory_entry>, bool>();
}
/*! \brief Asynchronous directory enumeration after a preceding operation.

\docs_enumerate

\return A `future<std::pair<std::vector<directory_entry>, bool>>`
\param _precondition The precondition to use.
\param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
\param _restart Restarts the enumeration for this open directory handle.
\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
\param _filtering Any filtering you want AFIO to do for you.
\ingroup enumerate
\qbk{distinguish, glob first}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
comparing inodes for equivalence to a direntry() won't help you.]
[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
birthtim, sparse, compressed.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
\exceptionmodelfree
\qexample{enumerate_example}
*/
inline future<std::pair<std::vector<directory_entry>, bool>> async_enumerate(future<> _precondition, path _glob, size_t _maxitems = 2, bool _restart = true,
  metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
{
  return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition));
}
/*! \brief Synchronous directory enumeration after a preceding operation.

\docs_enumerate

\return A vector of results and a bool indicating if there is more.
\param _precondition The precondition to use.
\param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
\param _restart Restarts the enumeration for this open directory handle.
\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
\param _filtering Any filtering you want AFIO to do for you.
\ingroup enumerate
\qbk{distinguish, glob first throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
comparing inodes for equivalence to a direntry() won't help you.]
[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
birthtim, sparse, compressed.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
\exceptionmodelfree
\qexample{enumerate_example}
*/
inline std::pair<std::vector<directory_entry>, bool> enumerate(future<> _precondition, path _glob, size_t _maxitems = 2, bool _restart = true,
  metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
{
  return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)).get();
}
/*! \brief Synchronous directory enumeration after a preceding operation.

\docs_enumerate

\return A vector of results and a bool indicating if there is more.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
\param _restart Restarts the enumeration for this open directory handle.
\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
\param _filtering Any filtering you want AFIO to do for you.
\ingroup enumerate
\qbk{distinguish, glob first non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
comparing inodes for equivalence to a direntry() won't help you.]
[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
birthtim, sparse, compressed.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
\exceptionmodelfree
\qexample{enumerate_example}
*/
inline std::pair<std::vector<directory_entry>, bool> enumerate(error_code &_ec, future<> _precondition, path _glob, size_t _maxitems = 2, bool _restart = true,
  metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
{
  auto ret = detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition));
  if (!(_ec = ret.get_error()))
    return ret.get();
  return std::pair<std::vector<directory_entry>, bool>();
}
/*! \brief Asynchronous directory enumeration after a preceding operation.

\docs_enumerate

\return A `future<std::pair<std::vector<directory_entry>, bool>>`
\param _precondition The precondition to use.
\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
\param _restart Restarts the enumeration for this open directory handle.
\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
\param _filtering Any filtering you want AFIO to do for you.
\ingroup enumerate
\qbk{distinguish, metadata first}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
comparing inodes for equivalence to a direntry() won't help you.]
[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
birthtim, sparse, compressed.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
\exceptionmodelfree
\qexample{enumerate_example}
*/
inline future<std::pair<std::vector<directory_entry>, bool>> async_enumerate(future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2, bool _restart = true,
  path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
{
  return detail::async_enumerate(_maxitems, _restart, _glob, _metadata, _filtering)(std::move(_precondition));
}
/*! \brief Synchronous directory enumeration after a preceding operation.

\docs_enumerate

\return A vector of results and a bool indicating if there is more.
\param _precondition The precondition to use.
\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
\param _restart Restarts the enumeration for this open directory handle.
\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
\param _filtering Any filtering you want AFIO to do for you.
\ingroup enumerate
\qbk{distinguish, metadata first throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
comparing inodes for equivalence to a direntry() won't help you.]
[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
birthtim, sparse, compressed.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
\exceptionmodelfree
\qexample{enumerate_example}
*/
inline std::pair<std::vector<directory_entry>, bool> enumerate(future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2,
  bool _restart = true, path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
{
  return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)).get();
}
/*! \brief Synchronous directory enumeration after a preceding operation.

\docs_enumerate

\return A vector of results and a bool indicating if there is more.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
\param _restart Restarts the enumeration for this open directory handle.
\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
\param _filtering Any filtering you want AFIO to do for you.
\ingroup enumerate
\qbk{distinguish, metadata first non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
comparing inodes for equivalence to a direntry() won't help you.]
[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
birthtim, sparse, compressed.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
\exceptionmodelfree
\qexample{enumerate_example}
*/
inline std::pair<std::vector<directory_entry>, bool> enumerate(error_code &_ec, future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2,
  bool _restart = true, path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
{
  auto ret = detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition));
  if (!(_ec = ret.get_error()))
    return ret.get();
  return std::pair<std::vector<directory_entry>, bool>();
}


/*! \brief Asynchronous extent enumeration after a preceding operation.

\docs_extents

\return A `future<std::vector<std::pair<off_t, off_t>>>`
\param _precondition The precondition to use.
\ingroup extents
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
into single extents.]
[raceguarantee Windows..Race free.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.}
\exceptionmodelfree
\qexample{extents_example}
*/
inline future<std::vector<std::pair<off_t, off_t>>> async_extents(future<> _precondition)
{
  return detail::async_extents()(std::move(_precondition));
}
/*! \brief Synchronous extent enumeration after a preceding operation.

\docs_extents

\return A vector of extents
\param _precondition The precondition to use.
\ingroup extents
\qbk{distinguish, throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
into single extents.]
[raceguarantee Windows..Race free.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.}
\exceptionmodelfree
\qexample{extents_example}
*/
inline std::vector<std::pair<off_t, off_t>> extents(future<> _precondition)
{
  return detail::async_extents()(std::move(_precondition)).get();
}
/*! \brief Synchronous extent enumeration after a preceding operation.

\docs_extents

\return A vector of extents
\param _ec Error code to set.
\param _precondition The precondition to use.
\ingroup extents
\qbk{distinguish, non throwing}
\raceguarantees{
[raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
into single extents.]
[raceguarantee Windows..Race free.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.}
\exceptionmodelfree
\qexample{extents_example}
*/
inline std::vector<std::pair<off_t, off_t>> extents(error_code &_ec, future<> _precondition)
{
  auto ret = detail::async_extents()(std::move(_precondition));
  if (!(_ec = ret.get_error()))
    return ret.get();
  return std::vector<std::pair<off_t, off_t>>();
}


/*! \brief Asynchronous volume enumeration after a preceding operation.

\docs_statfs

\return A `future<statfs_t>`
\param _precondition The precondition to use.
\param req A metadata request.
\ingroup statfs
\raceguarantees{
[raceguarantee FreeBSD, OS X..Race free.]
[raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
flags.rdonly, flags.noexec, flags.nosuid.]
[raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
is fetched separately.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
\exceptionmodelfree
\qexample{statfs_example}
*/
inline future<statfs_t> async_statfs(future<> _precondition, fs_metadata_flags req)
{
  return detail::async_statfs(req)(std::move(_precondition));
}
/*! \brief Synchronous volume enumeration after a preceding operation.

\docs_statfs

\return The volume metadata requested.
\param _precondition The precondition to use.
\param req A metadata request.
\ingroup statfs
\qbk{distinguish, throwing}
\raceguarantees{
[raceguarantee FreeBSD, OS X..Race free.]
[raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
flags.rdonly, flags.noexec, flags.nosuid.]
[raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
is fetched separately.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
\exceptionmodelfree
\qexample{statfs_example}
*/
inline statfs_t statfs(future<> _precondition, fs_metadata_flags req)
{
  return detail::async_statfs(req)(std::move(_precondition)).get();
}
/*! \brief Synchronous volume enumeration after a preceding operation.

\docs_statfs

\return The volume metadata requested.
\param _ec Error code to set.
\param _precondition The precondition to use.
\param req A metadata request.
\ingroup statfs
\qbk{distinguish, nonthrowing}
\raceguarantees{
[raceguarantee FreeBSD, OS X..Race free.]
[raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
flags.rdonly, flags.noexec, flags.nosuid.]
[raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
is fetched separately.]
}
\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
\exceptionmodelfree
\qexample{statfs_example}
*/
inline statfs_t statfs(error_code &_ec, future<> _precondition, fs_metadata_flags req)
{
  auto ret = detail::async_statfs(req)(std::move(_precondition));
  if (!(_ec = ret.get_error()))
    return ret.get();
  return statfs_t();
}

/*! \brief Make ready a future after a precondition future readies.

\return A future which returns out after precondition signals.
\param precondition The future which must signal before the returned future signals.
\param out The future to return.
\ingroup async_file_io_dispatcher
*/
inline future<> depends(future<> precondition, future<> out)
{
  return precondition.parent()->depends(precondition, out);
}

//! Utility routines often useful when using AFIO
namespace utils
{
  /*! \brief Returns the page sizes of this architecture which is useful for calculating direct i/o multiples.

  \param only_actually_available Only return page sizes actually available to the user running this process
  \return The page sizes of this architecture.
  \ingroup utils
  \complexity{Whatever the system API takes (one would hope constant time).}
  \exceptionmodel{Any error from the operating system or std::bad_alloc.}
  */
  BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC std::vector<size_t> page_sizes(bool only_actually_available = true) noexcept;

  /*! \brief Returns a reasonable default size for page_allocator, typically the closest page size from
  page_sizes() to 1Mb.

  \return A value of a TLB large page size close to 1Mb.
  \ingroup utils
  \complexity{Whatever the system API takes (one would hope constant time).}
  \exceptionmodel{Any error from the operating system or std::bad_alloc.}
  */
  inline size_t file_buffer_default_size() noexcept
  {
    static size_t size;
    if (!size)
    {
      std::vector<size_t> sizes(page_sizes(true));
      for (auto &i : sizes)
        if (i >= 1024 * 1024)
        {
          size = i;
          break;
        }
      if (!size)
        size = 1024 * 1024;
    }
    return size;
  }

  /*! \brief Fills the buffer supplied with cryptographically strong randomness. Uses the OS kernel API.

  \param buffer A buffer to fill
  \param bytes How many bytes to fill
  \ingroup utils
  \complexity{Whatever the system API takes.}
  \exceptionmodel{Any error from the operating system.}
  */
  BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC void random_fill(char *buffer, size_t bytes);

  /*! \brief Converts a number to a hex string. Out buffer can be same as in buffer.

  Note that the character range used is a 16 item table of:

  0123456789abcdef

  This lets one pack one byte of input into two bytes of output.

  \ingroup utils
  \complexity{O(N) where N is the length of the number.}
  \exceptionmodel{Throws exception if output buffer is too small for input.}
  */
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 6293) // MSVC sanitiser warns that we wrap n in the for loop
#endif
  inline size_t to_hex_string(char *out, size_t outlen, const char *_in, size_t inlen)
  {
    unsigned const char *in = (unsigned const char *) _in;
    static BOOST_CONSTEXPR_OR_CONST char table[] = "0123456789abcdef";
    if(outlen<inlen*2)
      BOOST_AFIO_THROW(std::invalid_argument("Output buffer too small."));
    for (size_t n = inlen - 2; n <= inlen - 2; n-=2)
    {
      out[n * 2 + 3] = table[(in[n+1] >> 4) & 0xf];
      out[n * 2 + 2] = table[in[n+1] & 0xf];
      out[n * 2 + 1] = table[(in[n] >> 4) & 0xf];
      out[n * 2 + 0] = table[in[n] & 0xf];
    }
    if(inlen&1)
    {
      out[1] = table[(in[0] >> 4) & 0xf];
      out[0] = table[in[0] & 0xf];
    }
    return inlen*2;
  }
#ifdef _MSC_VER
#pragma warning(pop)
#endif
  //! \overload
  inline std::string to_hex_string(std::string in)
  {
    std::string out(in.size() * 2, ' ');
    to_hex_string(const_cast<char *>(out.data()), out.size(), in.data(), in.size());
    return out;
  }

  /*! \brief Converts a hex string to a number. Out buffer can be same as in buffer.

  Note that this routine is about 43% slower than to_hex_string(), half of which is due to input validation.
  
  \ingroup utils
  \complexity{O(N) where N is the length of the string.}
  \exceptionmodel{Throws exception if output buffer is too small for input or input size is not multiple of two.}
  */
  inline size_t from_hex_string(char *out, size_t outlen, const char *in, size_t inlen)
  {
    if (inlen % 2)
      BOOST_AFIO_THROW(std::invalid_argument("Input buffer not multiple of two."));
    if (outlen<inlen / 2)
      BOOST_AFIO_THROW(std::invalid_argument("Output buffer too small."));
    bool is_invalid=false;
    auto fromhex = [&is_invalid](char c) -> unsigned char
    {
#if 1
      // ASCII starting from 48 is 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
      //                           48               65                              97
      static BOOST_CONSTEXPR_OR_CONST unsigned char table[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,                                                    // +10 = 58
        255, 255, 255, 255, 255, 255, 255,                                                                                                      // +7  = 65
        10, 11, 12, 13, 14, 15,                                                                                                                 // +6  = 71
        255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,       // +26 = 97
        10, 11, 12, 13, 14, 15
      };
      unsigned char r=255;
      if(c>=48 && c<=102)
        r=table[c-48];
      if(r==255)
        is_invalid=true;
      return r;
#else
      if(c>='0' && c<='9')
        return c-'0';
      if(c>='a' && c<='f')
        return c-'a'+10;
      if(c>='A' && c<='F')
        return c-'A'+10;
      BOOST_AFIO_THROW(std::invalid_argument("Input is not hexadecimal."));
#endif
    };
    for (size_t n = 0; n<inlen/2; n+=4)
    {
      unsigned char c[8];
      c[0]= fromhex(in[n * 2]);
      c[1]= fromhex(in[n * 2 + 1]);
      c[2]= fromhex(in[n * 2 + 2]);
      c[3]= fromhex(in[n * 2 + 3]);
      out[n]=(c[1]<<4)|c[0];
      c[4]= fromhex(in[n * 2 + 4]);
      c[5]= fromhex(in[n * 2 + 5]);
      out[n+1]=(c[3]<<4)|c[2];
      c[6]= fromhex(in[n * 2 + 6]);
      c[7]= fromhex(in[n * 2 + 7]);
      out[n+2]=(c[5]<<4)|c[4];
      out[n+3]=(c[7]<<4)|c[6];
    }
    for (size_t n = inlen/2-(inlen/2)%4; n<inlen/2; n++)
    {
      unsigned char c1 = fromhex(in[n * 2]), c2 = fromhex(in[n * 2 + 1]);
      out[n]=(c2<<4)|c1;
    }
    if(is_invalid)
      BOOST_AFIO_THROW(std::invalid_argument("Input is not hexadecimal."));
    return inlen/2;
  }

  /*! \brief Returns a cryptographically random string capable of being used as a filename. Essentially random_fill() + to_hex_string().

  \param randomlen The number of bytes of randomness to use for the string.
  \return A string representing the randomness at a 2x ratio, so if 32 bytes were requested, this string would be 64 bytes long.
  \ingroup utils
  \complexity{Whatever the system API takes.}
  \exceptionmodel{Any error from the operating system.}
  */
  inline std::string random_string(size_t randomlen)
  {
    size_t outlen = randomlen*2;
    std::string ret(outlen, 0);
    random_fill(const_cast<char *>(ret.data()), randomlen);
    to_hex_string(const_cast<char *>(ret.data()), outlen, ret.data(), randomlen);
    return ret;
  }

#ifndef BOOST_AFIO_SECDEC_INTRINSICS
# if defined(__GCC__) || defined(__clang__)
#  define BOOST_AFIO_SECDEC_INTRINSICS 1
# elif defined(_MSC_VER) && (defined(_M_X64) || _M_IX86_FP==1)
#  define BOOST_AFIO_SECDEC_INTRINSICS 1
# endif
#endif
#ifndef BOOST_AFIO_SECDEC_INTRINSICS
# define BOOST_AFIO_SECDEC_INTRINSICS 0
#endif
  /*! \class secded_ecc
  \brief Calculates the single error correcting double error detecting (SECDED) Hamming Error Correcting Code for a \em blocksize block of bytes. For example, a secdec_ecc<8> would be the very common 72,64 Hamming code used in ECC RAM, or secdec_ecc<4096> would be for a 32784,32768 Hamming code.
  
  Did you know that some non-ECC RAM systems can see 1e-12 flips/bit/hour, which is 3.3 bits flipped in a 16Gb RAM system
  per 24 hours). See Schroeder, Pinheiro and Weber (2009) 'DRAM Errors in the Wild: A Large-Scale Field Study'.

  After construction during which lookup tables are built, no state is modified and therefore this class is safe for static
  storage (indeed if C++ 14 is available, the constructor is constexpr). The maximum number of bits in a code is a good four
  billion, I did try limiting it to 65536 for performance but it wasn't worth it, and one might want > 8Kb blocks maybe.
  As with all SECDED ECC, undefined behaviour occurs when more than two bits of error are present or the ECC supplied
  is incorrect. You should combine this SECDED with a robust hash which can tell you definitively if a buffer is error
  free or not rather than relying on this to correctly do so.
  
  The main intended use case for this routine is calculating the ECC on data being written to disc, and hence that is
  where performance has been maximised. It is not expected that this routine will be frequently called on data being read
  from disc i.e. only when its hash doesn't match its contents which should be very rare, and then a single bit heal using this routine is attempted
  before trying again with the hash. Care was taken that really enormous SECDEDs are fast, in fact tuning was mostly
  done for the 32784,32768 code which can heal one bad bit per 4Kb page as the main thing we have in mind is achieving
  reliable filing system code on computers without ECC RAM and in which sustained large quantities of random disc i/o produce
  a worrying number of flipped bits in a 24 hour period (anywhere between 0 and 3 on my hardware here, average is about 0.8).
  
  Performance of the fixed block size routine where you supply whole chunks of \em blocksize is therefore \b particularly excellent
  as I spent a lot of time tuning it for Ivy Bridge and later out of order architectures: an
  amazing 22 cycles per byte for the 32784,32768 code, which is a testament to modern out of order CPUs (remember SECDED inherently must work a bit
  at a time, so that's just 2.75 amortised CPU cycles per bit which includes a table load, a bit test, and a conditional XOR)
  i.e. it's pushing about 1.5 ops per clock cycle. On my 3.9Ghz i7-3770K here, I see about 170Mb/sec per CPU core.
  
  The variable length routine is necessarily much slower as it must work in single bytes, and achieves 72 cycles per byte,
  or 9 cycles per bit (64Mb/sec per CPU core).

  \ingroup utils
  \complexity{O(N) where N is the blocksize}
  \exceptionmodel{Throws constexpr exceptions in constructor only, otherwise entirely noexcept.}
  */
  template<size_t blocksize> class secded_ecc
  {
  public:
    typedef unsigned int result_type; //!< The largest ECC which can be calculated
  private:
    static BOOST_CONSTEXPR_OR_CONST size_t bits_per_byte=8;
    typedef unsigned char unit_type;  // The batch unit of processing
    result_type bitsvalid;
    // Many CPUs (x86) are slow doing variable bit shifts, so keep a table
    result_type ecc_twospowers[sizeof(result_type)*bits_per_byte];
    unsigned short ecc_table[blocksize*bits_per_byte];
    static bool _is_single_bit_set(result_type x)
    {
#ifndef _MSC_VER
#if defined(__i386__) || defined(__x86_64__)
#ifndef __SSE4_2__
      // Do a once off runtime check
      static int have_popcnt=[]{
        size_t cx, dx;
#if defined(__x86_64__)
        asm("cpuid": "=c" (cx), "=d" (dx) : "a" (1), "b" (0), "c" (0), "d" (0));
#else
        asm("pushl %%ebx\n\tcpuid\n\tpopl %%ebx\n\t": "=c" (cx), "=d" (dx) : "a" (1), "c" (0), "d" (0));
#endif
        return (dx&(1<<26))!=0/*SSE2*/ && (cx&(1<<23))!=0/*POPCNT*/;
      }();
      if(have_popcnt)
#endif
      {
        unsigned count;
        asm("popcnt %1,%0" : "=r"(count) : "rm"(x) : "cc");
        return count==1;
      }
#endif
      return __builtin_popcount(x)==1;
#else
      x -= (x >> 1) & 0x55555555;
      x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
      x = (x + (x >> 4)) & 0x0f0f0f0f;
      unsigned int count=(x * 0x01010101)>>24;
      return count==1;
#if 0
      x -= (x >> 1) & 0x5555555555555555ULL;
      x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL);
      x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL;
      unsigned long long count=(x * 0x0101010101010101ULL)>>56;
      return count==1;
#endif
#endif
    }
  public:
    //! Constructs an instance, configuring the necessary lookup tables
    BOOSTLITE_CONSTEXPR secded_ecc()
    {
      for(size_t n=0; n<sizeof(result_type)*bits_per_byte; n++)
        ecc_twospowers[n]=((result_type)1<<n);
      result_type length=blocksize*bits_per_byte;
      // This is (data bits + parity bits + 1) <= 2^(parity bits)
      for(result_type p=1; p<sizeof(result_type)*bits_per_byte; p++)
        if((length+p+1)<=ecc_twospowers[p])
        {
          bitsvalid=p;
          break;
        }
      if((bits_per_byte-1+bitsvalid)/bits_per_byte>sizeof(result_type))
        BOOST_AFIO_THROW(std::runtime_error("ECC would exceed the size of result_type!"));
      for(result_type i=0; i<blocksize*bits_per_byte; i++)
      {
        // Make a code bit
        result_type b=i+1;
#if BOOST_AFIO_SECDEC_INTRINSICS && 0 // let constexpr do its thing
#ifdef _MSC_VER
        unsigned long _topbit;
        _BitScanReverse(&_topbit, b);
        result_type topbit=_topbit;
#else
        result_type topbit=bits_per_byte*sizeof(result_type)-__builtin_clz(b);
#endif
        b+=topbit;
        if(b>=ecc_twospowers[topbit]) b++;
        //while(b>ecc_twospowers(_topbit+1)) _topbit++;
        //b+=_topbit;
        //if(b>=ecc_twospowers(_topbit)) b++;
#else
        for(size_t p=0; ecc_twospowers[p]<(b+1); p++)
          b++;
#endif
        ecc_table[i]=(unsigned short) b;
        if(b>(unsigned short)-1)
          BOOST_AFIO_THROW(std::runtime_error("Precalculated table has exceeded its bounds"));
      }
    }
    //! The number of bits valid in result_type
    constexpr result_type result_bits_valid() const noexcept
    {
      return bitsvalid;
    }
    //! Accumulate ECC from fixed size buffer
    result_type operator()(result_type ecc, const char *buffer) const noexcept
    {
      if(blocksize<sizeof(unit_type)*8)
        return (*this)(ecc, buffer, blocksize);
      // Process in lumps of eight
      const unit_type *_buffer=(const unit_type *) buffer;
//#pragma omp parallel for reduction(^:ecc)
      for(size_t i=0; i<blocksize; i+=sizeof(unit_type)*8)
      {
        union { unsigned long long v; unit_type c[8]; };
        result_type prefetch[8];
        v=*(unsigned long long *)(&_buffer[0+i/sizeof(unit_type)]); // min 1 cycle
#define BOOST_AFIO_ROUND(n) \
          prefetch[0]=ecc_table[(i+0)*8+n]; \
          prefetch[1]=ecc_table[(i+1)*8+n]; \
          prefetch[2]=ecc_table[(i+2)*8+n]; \
          prefetch[3]=ecc_table[(i+3)*8+n]; \
          prefetch[4]=ecc_table[(i+4)*8+n]; \
          prefetch[5]=ecc_table[(i+5)*8+n]; \
          prefetch[6]=ecc_table[(i+6)*8+n]; \
          prefetch[7]=ecc_table[(i+7)*8+n]; \
          if(c[0]&((unit_type)1<<n))\
            ecc^=prefetch[0];\
          if(c[1]&((unit_type)1<<n))\
            ecc^=prefetch[1];\
          if(c[2]&((unit_type)1<<n))\
            ecc^=prefetch[2];\
          if(c[3]&((unit_type)1<<n))\
            ecc^=prefetch[3];\
          if(c[4]&((unit_type)1<<n))\
            ecc^=prefetch[4];\
          if(c[5]&((unit_type)1<<n))\
            ecc^=prefetch[5];\
          if(c[6]&((unit_type)1<<n))\
            ecc^=prefetch[6];\
          if(c[7]&((unit_type)1<<n))\
            ecc^=prefetch[7];
        BOOST_AFIO_ROUND(0)                                                    // prefetch = min 8, bit test and xor = min 16, total = 24
        BOOST_AFIO_ROUND(1)
        BOOST_AFIO_ROUND(2)
        BOOST_AFIO_ROUND(3)
        BOOST_AFIO_ROUND(4)
        BOOST_AFIO_ROUND(5)
        BOOST_AFIO_ROUND(6)
        BOOST_AFIO_ROUND(7)
  #undef BOOST_AFIO_ROUND                                                      // total should be 1+(8*24/3)=65
      }
      return ecc;
    }
    result_type operator()(const char *buffer) const noexcept { return (*this)(0, buffer); }
    //! Accumulate ECC from partial buffer where \em length <= \em blocksize
    result_type operator()(result_type ecc, const char *buffer, size_t length) const noexcept
    {
      const unit_type *_buffer=(const unit_type *) buffer;
//#pragma omp parallel for reduction(^:ecc)
      for(size_t i=0; i<length; i+=sizeof(unit_type))
      {
        unit_type c=_buffer[i/sizeof(unit_type)];                 // min 1 cycle
        if(!c)                                                    // min 1 cycle
          continue;
        char bitset[bits_per_byte*sizeof(unit_type)];
        result_type prefetch[bits_per_byte*sizeof(unit_type)];
        // Most compilers will roll this out
        for(size_t n=0; n<bits_per_byte*sizeof(unit_type); n++)   // min 16 cycles
        {
          bitset[n]=!!(c&((unit_type)1<<n));
          prefetch[n]=ecc_table[i*bits_per_byte+n];               // min 8 cycles
        }
        result_type localecc=0;
        for(size_t n=0; n<bits_per_byte*sizeof(unit_type); n++)
        {
          if(bitset[n])                                           // min 8 cycles
            localecc^=prefetch[n];                                // min 8 cycles
        }
        ecc^=localecc;                                            // min 1 cycle. Total cycles = min 43 cycles/byte
      }
      return ecc;
    }
    result_type operator()(const char *buffer, size_t length) const noexcept { return (*this)(0, buffer, length); }
    //! Given the original ECC and the new ECC for a buffer, find the bad bit. Return (result_type)-1 if not found (e.g. ECC corrupt)
    result_type find_bad_bit(result_type good_ecc, result_type bad_ecc) const noexcept
    {
      result_type length=blocksize*bits_per_byte, eccdiff=good_ecc^bad_ecc;
      if(_is_single_bit_set(eccdiff))
        return (result_type)-1;
      for(result_type i=0, b=1; i<length; i++, b++)
      {
        // Skip parity bits
        while(_is_single_bit_set(b))
          b++;
        if(b==eccdiff)
          return i;
      }
      return (result_type)-1;
    }
    //! The outcomes from verify()
    enum verify_status
    {
      corrupt=0,  //!< The buffer had more than a single bit corrupted or the ECC was invalid
      okay=1,     //!< The buffer had no errors
      healed=2    //!< The buffer was healed
    };
    //! Verifies and heals when possible a buffer, returning non zero if the buffer is error free
    verify_status verify(char *buffer, result_type good_ecc) const noexcept
    {
      result_type this_ecc=(*this)(0, buffer);
      if(this_ecc==good_ecc)
        return verify_status::okay; // no errors
      result_type badbit=find_bad_bit(good_ecc, this_ecc);
      if((result_type)-1==badbit)
        return verify_status::corrupt; // parity corrupt?
      buffer[badbit/bits_per_byte]^=(unsigned char) ecc_twospowers[badbit%bits_per_byte];
      this_ecc=(*this)(0, buffer);
      if(this_ecc==good_ecc)
        return healed; // error healed
      // Put the bit back
      buffer[badbit/bits_per_byte]^=(unsigned char) ecc_twospowers[badbit%bits_per_byte];
      return verify_status::corrupt; // more than one bit was corrupt
    }
  };
 
  namespace detail
  {
    struct large_page_allocation
    {
      void *p;
      size_t page_size_used;
      size_t actual_size;
      large_page_allocation() : p(nullptr), page_size_used(0), actual_size(0) { }
      large_page_allocation(void *_p, size_t pagesize, size_t actual) : p(_p), page_size_used(pagesize), actual_size(actual) { }
    };
    inline large_page_allocation calculate_large_page_allocation(size_t bytes)
    {
      large_page_allocation ret;
      auto pagesizes(page_sizes());
      do
      {
        ret.page_size_used=pagesizes.back();
        pagesizes.pop_back();
      } while(!pagesizes.empty() && !(bytes/ret.page_size_used));
      ret.actual_size=(bytes+ret.page_size_used-1)&~(ret.page_size_used-1);
      return ret;    
    }
    BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC large_page_allocation allocate_large_pages(size_t bytes);
    BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC void deallocate_large_pages(void *p, size_t bytes);
  }
  /*! \class page_allocator
  \brief An STL allocator which allocates large TLB page memory.
  \ingroup utils

  If the operating system is configured to allow it, this type of memory is particularly efficient for doing
  large scale file i/o. This is because the kernel must normally convert the scatter gather buffers you pass
  into extended scatter gather buffers as the memory you see as contiguous may not, and probably isn't, actually be
  contiguous in physical memory. Regions returned by this allocator \em may be allocated contiguously in physical
  memory and therefore the kernel can pass through your scatter gather buffers unmodified.

  A particularly useful combination with this allocator is with the page_sizes() member function of __afio_dispatcher__.
  This will return which pages sizes are possible, and which page sizes are enabled for this user. If writing a
  file copy routine for example, using this allocator with the largest page size as the copy chunk makes a great
  deal of sense.

  Be aware that as soon as the allocation exceeds a large page size, most systems allocate in multiples of the large
  page size, so if the large page size were 2Mb and you allocate 2Mb + 1 byte, 4Mb is actually consumed.
  */
  template <typename T>
  class page_allocator
  {
  public:
      typedef T         value_type;
      typedef T*        pointer;
      typedef const T*  const_pointer;
      typedef T& reference;
      typedef const T&  const_reference;
      typedef size_t    size_type;
      typedef ptrdiff_t difference_type;
      typedef std::true_type propagate_on_container_move_assignment;
      typedef std::true_type is_always_equal;

      template <class U>
      struct rebind { typedef page_allocator<U> other; };

      page_allocator() noexcept
      {}

      template <class U>
      page_allocator(const page_allocator<U>&) noexcept
      {}

      size_type
      max_size() const noexcept
      { return size_type(~0) / sizeof(T); }

      pointer
      address(reference x) const noexcept
      { return std::addressof(x); }

      const_pointer
      address(const_reference x) const noexcept
      { return std::addressof(x); }

      pointer
      allocate(size_type n, const void *hint = 0)
      {
          if(n>max_size())
              throw std::bad_alloc();
          auto mem(detail::allocate_large_pages(n * sizeof(T)));
          if (mem.p == nullptr)
              throw std::bad_alloc();
          return reinterpret_cast<pointer>(mem.p);
      }

      void
      deallocate(pointer p, size_type n)
      {
          if(n>max_size())
              throw std::bad_alloc();
          detail::deallocate_large_pages(p, n * sizeof(T));
      }

      template <class U, class ...Args>
      void
      construct(U* p, Args&&... args)
      { ::new(reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...); }

      template <class U> void
      destroy(U* p)
      { p->~U(); }
  };
  template <>
  class page_allocator<void>
  {
  public:
      typedef void         value_type;
      typedef void*        pointer;
      typedef const void*  const_pointer;
      typedef std::true_type propagate_on_container_move_assignment;
      typedef std::true_type is_always_equal;

      template <class U>
      struct rebind { typedef page_allocator<U> other; };
  };
  template<class T, class U> inline bool operator==(const page_allocator<T> &, const page_allocator<U> &) noexcept { return true; }
}


BOOST_AFIO_V2_NAMESPACE_END

// Specialise std::hash<> for directory_entry
#ifndef BOOST_AFIO_DISABLE_STD_HASH_SPECIALIZATION
#include <functional>
namespace std
{
    template<> struct hash<BOOST_AFIO_V2_NAMESPACE::path>
    {
    public:
        size_t operator()(const BOOST_AFIO_V2_NAMESPACE::path &p) const
        {
            return BOOST_AFIO_V2_NAMESPACE::path_hash()(p);
        }
    };
    template<> struct hash<BOOST_AFIO_V2_NAMESPACE::directory_entry>
    {
    public:
        size_t operator()(const BOOST_AFIO_V2_NAMESPACE::directory_entry &p) const
        {
            return BOOST_AFIO_V2_NAMESPACE::directory_entry_hash()(p);
        }
    };

}//namesapce std
#endif

#ifdef BOOST_MSVC
#pragma warning(pop)
#endif

#if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
#undef BOOST_AFIO_VALIDATE_INPUTS // Let BOOST_AFIO_NEVER_VALIDATE_INPUTS take over
#define BOOST_AFIO_HEADER_INCLUDED 1
#include "detail/impl/afio.ipp"
#undef BOOST_AFIO_HEADER_INCLUDED
#endif

#endif
#endif
